interface Parseable {
public abstract void parse(String fileName);
}
public ParserManager {
public static Parseable getParser(String type) {
if(type.equals("XML") {
return new XMLParser();
} else {
return new HTMLParser():
}
}
Parseable parser = ParserManager.getParser("XML");
// 객체 지향적이지 않은 코드
Parseable parser = new XMLParser(); -> 이렇게 하면 HTMLParser로 변경 시 클라이언트 코드가 바뀐다
아래는 예제 코드이다.
public interface I {
public abstract void play();
}
public class A {
void play(I i) {
i.play();
}
}
public class B implements I{
@Override
public void play() {
System.out.println("B play");
}
}
public static void main(String[] args) {
A a = new A();
a.autoPlay(new B());
}
위의 코드를 유심히 보자. 뭔가 이상하지 않은가?
A class는 B의 존재에 대해서 몰라도 되지만, 실제 A를 사용하는 main 메서드에서는 a.autoPlay(new B());
에서 new B()
와 같이 실제 구현체를 알아야 한다.
그럼 해당 구현체가 아닌 다른 구현체를 쓰고 싶다면? 결국 메인 메서드에서 실제 구현체에 대한 코드를 바꿔야 한다. 이를 해결할 좋은 방법이 없을까?
다시 이전 Parser 예제에 정답이 있다.
public ParserManager {
public static Parseable getParser(String type) {
if(type.equals("XML") {
return new XMLParser();
} else {
return new HTMLParser():
}
}
바로 ParserManager 같은 외부에서 실제 구현체를 주입을 도와주는 객체가 있으면 된다.
그래서 Spring 에서는 이를 도와주는 스프링 컨테이너가 있다.
public interface I {
public abstract void play();
}
@Component
public class A {
@Autowired // 생성자로 의존 관계를 주입한다.
public A(I i) {
this.i = i;
}
void autoPlay(I i) {
i.play();
}
}
@Component
public class B implements I{
@Override
public void play() {
System.out.println("B play");
}
}
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
A a = context.getBean(A.class);
a.autoPlay();
}
AnnotationConfigApplicationContext
는 스프링 컨테이너를 초기화하고, AppConfig
클래스를 통해 빈(Bean) 설정을 로드한다.context.getBean(A.class);
코드로 스프링 컨테이너에서 A 타입의 빈을 가져온다. 이렇게 가져온 빈은 이미 필요한 의존성이 주입된 상태이다.DI(Dependency Injection, 의존 관계 주입, 예제에선 I 클래스의 실제 구현체를 외부에서 주입하는 것)을 스프링 컨테이너가 해주는 것이고 이는 IoC(Inverse of Controll, 제어의 역전, 개발자가 작성한 객체나 메서드의 제어를 개발자가 아니라 외부에 위임(스프링 컨테이너)하는 설계 원칙)의 한 형태이다.
개인적으로 인터페이스 부분은 정말 매번 볼 때마다 그 뜻이 새로운 것 같다.
‘처음 봤을 땐 이걸 어디다가 사용할까?’라고 생각했다. 시간이 지나고 많은 학습을 하고, 프로젝트를 하면서 직접 코드를 수정하고, 분해하는 유지보수의 과정을 거치면서 객체지향적인 코드의 필요성을 알게 되고, 다시끔 자바의 정석을 펴 이 부분을 보니 정말 새로웠다.
또한, 그저 그렇구나 하고 넘겼던 부분들을 이제와 곱씹어 보니 모두가 연결 되어있었구나. 라고 생각이 들게 된다.
예전 콜라와 펩시콜라, 코카콜라 같은 코드 예제를 보면서 DI와 IoC를 이해하던 시간이 있었는데, 이렇게 추상클래스부터 맥락을 따라 내려오면서 공부하다 보니 좀 더 이해가 쉬운거 같다.
맥락을 짚으면서 깊게 내려가는 공부를 하자 !