
Bean을 공부하기전에 나오는 얘기가 있었다. 바로 IoC와 DI이다. 그런데 대체 저게 뭐지...?😐
스프링을 공부하다 보면 저 개념이 계속 나와서 한번 정리하고 이해하는 시간이 필요해 정리해 보았다!!
객체지향의 SOLID 원칙 그리고 GoF 의 디자인 패턴과 같은 설계 원칙 및 디자인 패턴
IoC는 설계 원칙에 해당하고 DI는 디자인 패턴
김치 볶음밥 맛있게 만드는 방법 (설계 원칙)
🧑🍳 맛있는 김치 볶음밥을 만들기 위한 원칙
- 신선한 재료를 사용한다.
- 신 김치를 사용한다.
- 밥과 김치의 비율을 잘 맞춰야 한다.
- 볶을 때 재료의 순서가 중요하다.
김치 볶음밥 레시피 (디자인 패턴)
🍳 맛있는 김치 볶음밥을 만들기 위한 황금 레시피
- 오일을 두른 팬에 채썬 파를 볶아 파기름을 만든다.
- 준비한 햄을 넣고 볶다가, 간장 한스푼을 넣어 풍미를 낸다.
- 설탕에 버무린 김치를 넣고 함께 볶는다.
- 미리 식혀 둔 밥을 넣어 함께 볶는다.
- 참기름 한스푼을 넣어 마무리한다.
DI 패턴을 사용하여 IoC 설계 원칙을 구현하고 있다’
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 가 치킨이 아니라 피자를 먹고 싶어 한다면? 많은 수의 코드 변경이 불가피!
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();
}
}
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("피자를 먹는다.");
}
}
Setter 메서드를 통한 주입
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();
}
}
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("피자를 먹는다.");
}
}
생성자를 통한 주입(가장 권장)
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();
}
}
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("피자를 먹는다.");
}
}
이전에는 Consumer가 직접 Food를 만들어 먹었기 때문에 새로운 Food를 만들려면 추가적인 요리준비(코드변경)가 불가피했다.
이를 해결하기 위해 만들어진 Food를 Consumer에게 전달해주는 식으로 변경함으로써 Consumer는 추가적인 요리준비(코드변경) 없이 어느 Food가 되었든지 전부 먹을 수 있게 되었다.
제어의 역전
제어의 역전은 다른 객체를 직접 생성하거나 제어하는 것이 아니라 외부에서 관리하는 객체를 가져와 사용하는 것 == 2번!!
1.클래스 A에서 클래스 B객체 생성
public class A {
b = new B();
}
2.스프링 컨테이너가 객체를 관리하는 방식
public class A {
private B b;
}
클래스 B 객체를 직접 생성하는 것이 아니므로, 어딘가에서 받아와서 사용하고 있다고 추측
제어의 역전을 적용하면 객체를 외부에서 관리하게 되고, 실제로 사용할 때에는 외부에서 제공해주는 객체를 받아오게 된다.
IoC 개념 도입의 장점