좋은 코드란? 간단한 논리, 중복 없는 명확한 표현 사용, 이해와 수정이 쉬워야 함 등등
좋은 코드를 작성하기 위해서는 신경 써야 하는 부분이 많다.
이를 도와주는 것이 Spring Framework이다. 그리고 이때 제공하는 핵심 기술들 중 하나가 바로 IoC와 DI이다.
IoC, DI는 객체지향의 SOLID 원칙 그리고 GoF의 디자인 패턴과 같은 설계 원칙 및 디자인 패턴이다.
IoC는 설계 원칙, DI는 디자인 패턴에 해당한다.
public class Consumer {
void eat() {
Chicken chicken = new Chicken();
chicken.eat();
}
public static void main(String[] args) {
Consumer consumer = new Consumer();
consumer.eat();
}
}
class Chicken {
public void eat() {
System.out.println("치킨을 먹는다.");
}
}
위 예시 코드를 살펴보면 Consumer와 Chicken의 관계는 강하게 연결되어 있는 것을 알 수 있다.
이때 Consumer가 다른 것을 먹고 싶어할 때, 굉장히 많은 코드의 변경이 필요하게 된다.
이를 해결하기 위해서 우리는 Java Interface를 활용했다.
public class Consumer {
void eat(Food food) {
food.eat();
}
public static void main(String[] args) {
Consumer consumer = new Consumer();
consumer.eat(new Chicken());
consumer.eat(new Pizza());
}
}
interface Food {
void eat();
}
class Chicken implements Food{
@Override
public void eat() {
System.out.println("치킨을 먹는다.");
}
}
class Pizza implements Food{
@Override
public void eat() {
System.out.println("피자를 먹는다.");
}
}
이처럼 Interface 다형성의 원리를 사용하여 구현하면 고객이 어떠한 음식을 요구하더라도 쉽게 대처할 수 있다.
이와 같은 관계를 약한 결합 및 약한 의존성이라고 한다.
코드에서의 주입이란 여러 방법을 통해 필요로 하는 객체를 해당 객체에 전달하는 것을 의미한다.
public class Consumer {
Food food;
void eat() {
this.food.eat();
}
public static void main(String[] args) {
Consumer consumer = new Consumer();
consumer.food = new Chicken();
consumer.eat();
consumer.food = new Pizza();
consumer.eat();
}
}
public class Consumer {
Food food;
void eat() {
this.food.eat();
}
// set 메서드를 통해서 필요 객체를 주입 받음
public void setFood(Food food) {
this.food = food;
}
public static void main(String[] args) {
Consumer consumer = new Consumer();
consumer.setFood(new Chicken());
consumer.eat();
consumer.setFood(new Pizza());
consumer.eat();
}
}
public class Consumer {
Food food;
public Consumer(Food food) {
this.food = food;
}
void eat() {
this.food.eat();
}
public static void main(String[] args) {
Consumer consumer = new Consumer(new Chicken());
consumer.eat();
consumer = new Consumer(new Pizza());
consumer.eat();
}
}
위 코드를 잠시 보면 기존에는 Consumer가 직접 Food를 만들어서 먹었기에 새로운 Food를 만들려면 추가적인 코드 변경이 불가피했다.
이때는 제어의 흐름이 Consumer -> Food 라고 볼 수 있다.
하지만 Food Interface를 통해 이를 Consumer에게 전달해주는 식으로 변경이 됨으로써 Consumer는 추가적인 코드변경 없이 어느 Food가 오던 간에 전부 수용할 수 있게 되었다.
이때 제어의 흐름이 Food -> Consumer 라고 볼 수 있다.
이처럼 제어의 흐름이 반대가 되는 것을 제어의 역전이라 한다.