일반적으로 프로그래밍에서 어떤 객체가 다른 객체를 생성하거나, 호출하거나, 관리하는 제어를 가지고 있다. 이를 제어의 순환 이라고 생각할 수 있다. 그러나 IoC에서는 이러한 제어의 흐름이 역전되어 있다. 즉, 객체가 자신의 제어 흐름을 직접 관리하지 않고 외부에서 관리되는 것을 의미한다.
예를 들어, 우리가 레스토랑에 가서 음식을 주문한다고 가정하자.
일반적으로 손님이 주문을 하면 주방에서 요리가 준비 되고, 서빙 직원이 손님에게 음식을 가져다 주는 흐름이 일어난다.
이것이 일반적인 제어 흐름이다.
그러나 IoC 개념을 적용해본다고 하면, 손님은 주문만 하고, 주방에서는 요리가 준비되고, 서빙 직원이 음식을 가져다 주는 것을 완전히 외부에서 제어한다.
즉, 주문하는 사람(객체)은 제어의 흐름을 직접 관리하지 않고, 외부에서 이루어 지는것을 의미한다.
일반적으로 객체가 다른 객체를 생성하고, 호출하고, 관리하는 제어의 흐름이 일어난다. 그러나 Ioc에서는 객체가 자신의 제어 흐름을 직접 관리하지 않고 외부에서 관리된다. 예를 들어, 의존성 관리(Dependency Injection)이라는 기술을 사용하여 객체가 필요로 하는 의존성을 외부에서 주입받게 된다. 이렇게 하면 객체는 자신의 제어 흐름을 외부로부터 받게 되어 제어의 역전이 이루어진다.
<Ioc가 적용되지 않은 코드>
// 주문하는 사람 클래스
public class Customer {
// 주문 메서드
public void placeOrder() {
System.out.println("음료를 주문합니다.");
// 커피를 준비하는 메서드 직접 호출
Coffee coffee = new Coffee();
coffee.prepare();
}
}
// 음료 클래스의 구현체: 커피
public class Coffee {
// 커피를 준비하는 메서드
public void prepare() {
System.out.println("커피를 준비합니다.");
}
}
// 음료 클래스의 구현체: 차
public class Tea {
// 차를 준비하는 메서드
public void prepare() {
System.out.println("차를 준비합니다.");
}
}
// 메인 클래스
public class Main {
public static void main(String[] args) {
// 주문하는 사람 객체 생성
Customer customer = new Customer();
// 주문 수행
customer.placeOrder();
}
}
위의 예시 코드에서 Customer 클래스가 직접 Coffee 객체를 생성하고, Coffee 객체의 메서드를 호출한다. 이렇게 되면, Customer 클래스가 Coffee 클래스에 의존하고 있으며, Customer 클래스가 직접 객체를 생성하고 관리하는 제어 흐름이 일어나고 있다. 이러한 경우 IoC가 적용되지 않은 상태이다. 객체간의 의존성이 강하게 결합되어 유연성이 떨어지고, 코드의 재사용성과 테스트 용이성이 저하될수 있다.
<Ioc가 적용된 코드>
// 주문하는 사람 클래스
public class Customer {
private Drink drink; // 음료 객체에 대한 의존성
// 생성자를 통한 의존성 주입
public Customer(Drink drink) {
this.drink = drink;
}
// 주문 메서드
public void placeOrder() {
System.out.println("음료를 주문합니다.");
drink.prepare(); // 주문한 음료를 준비하는 메서드 호출
}
}
// 음료 클래스
public interface Drink {
void prepare();
}
// 음료 클래스의 구현체: 커피
public class Coffee implements Drink {
@Override
public void prepare() {
System.out.println("커피를 준비합니다.");
}
}
// 음료 클래스의 구현체: 차
public class Tea implements Drink {
@Override
public void prepare() {
System.out.println("차를 준비합니다.");
}
}
// 메인 클래스
public class Main {
public static void main(String[] args) {
// 커피 객체 생성
Drink coffee = new Coffee();
// 주문하는 사람 객체 생성 (커피 객체를 주입하여)
Customer customer1 = new Customer(coffee);
// 주문 수행
customer1.placeOrder();
// 차 객체 생성
Drink tea = new Tea();
// 주문하는 사람 객체 생성 (차 객체를 주입하여)
Customer customer2 = new Customer(tea);
// 주문 수행
customer2.placeOrder();
}
}
위 예시 코드에서는 Customer 클래스가 Drink 인터페이스를 의존하고 있다. 이 때, Customer 클래스는 직접 Coffee나 Tea 객체를 생성하거나 관리하지 않고, 생성자를 통해서 Drink 인터페이스에 대한 구현체를 주입 받는다. 이것이 의존성 주입을 통한 제어의 역전(IoC)이다. 이렇게 구조를 만들면 코드가 유연해지고, 재사용성이 향상된다.