장난감 자동차를 만든다 생각해보죠🚗

장난감 자동차를 만들기 위해 설계도면을 작성합니다

class DuracellUltraBattery implements Battery{
	//대충 베터리에 대한 멤버변수 및 메서드
}

class Car{
	//대충 장난감 자동차에 필요한 재료들을 멤버변수로선언
    private DuracellUltraBattery battery;//베터리
    
    public Car(){
   		//대충 다른 멤버변수들 초기화하는 코드
        battery = new DuracellUltraBattery();//베터리를 듀라셀 울트라 베터리 타입으로 할당
    }
	
    //대충 장난감 자동차가 동작하는 메서드
}

이런 설계도면을 가지고 공장을 마구마구 돌려서 장난감 자동차를 찍어내 시중에 판매합니다.

public class Factory{
	public static void main(String[] args){
    	
        while(true){//공장은 쉴틈없이 돌아가야죠
        	Car car = new Car();//마구마구 장난감을 찍고
            
            //대충 포장하는 코드
            
            //대충 트럭으로 나르는 코드
        }
    }
}

요구사항 변경🔄

듀라셀은 너무 비싸서 마진이 남지 않아!! 값이 싼 Bexel로 바꿔!

설계도대로 자동차를 찍어내던 공장을 멈추고,,,, 설계도면을 다시 힘겹게 수정한 뒤,,,, 다시 공장을 돌립니다.
이 과정에서 수많은 손실이 발생해버렸죠..

class BexcelBattery implements Battery{
	//대충 베터리에 대한 멤버변수 및 메서드
}

class Car{
	//대충 장난감 자동차에 필요한 재료들을 멤버변수로선언
    private BexcelBattery battery;//베터리
    
    public Car(){
   		//대충 다른 멤버변수들 초기화하는 코드
        battery = new BexcelBattery();//베터리를 벡셀 베터리 타입으로 할당
    }
	
    //대충 장난감 자동차가 동작하는 메서드
}

손해를 보지 않고 건전지를 교체할 수 있을까 🤷‍♂️

장난감차 설계도에 건전지의 종류가 무엇인지 명시를 해줘야하나...? 라는 생각이 들겁니다
자동차에서의 관심사는 건전지의 종류가 아닙니다. 자동차는 건전지가 오래가는 듀라셀인지, 아니면 값이 싼 Bexel인지, 전혀 관심사가 아닙니다.
그저 사이즈가 맞는 "건전지" 역할을 할 수 있는 물건이 오면 그 건전지가 무엇이 오든 상관이 없지 않을까요?

따라서 자동차의 설계도에서 건전지를 직접 명시하는것이 아닌, 건전지 틀을 설계도에 넣는것입니다.

class Car{
	//대충 장난감 자동차에 필요한 재료들을 멤버변수로선언
    private Battery battery;//건전지 틀
    
    public Car(Battery battery){
   		//대충 다른 멤버변수들 초기화하는 코드
        this.battery = battery;//건전지 타입이기만 하다면 어떤 건전지든 상관없다!
    }
	
    //대충 장난감 자동차가 동작하는 메서드
}

그런 뒤, 공장 과정에서 건전지를 끼워 넣는 과정을 통해서 자동차를 완성시키는 것이죠!

public class Factory{
	public static void main(String[] args){
    	Battery bexel = new BexelBattery();//공장에서 Bexel 베터리를 준비해놓는다!
        while(true){//공장은 쉴틈없이 돌아가야죠
        	Car car = new Car(bexel);//마구마구 장난감을 찍으며, 건전지로는 Bexel을 넣는다
            
            //대충 포장하는 코드
            
            //대충 트럭으로 나르는 코드
        }
    }
}

이렇게 되면 공장에서 건전지 타입만 바꿔서 끼워넣어준다면 설계도면을 건드리지 않고 공장을 가동시킬 수 있죠!

그래서 IOC하고 DI가 뭔데?

IOC는 Inversion Of Control의 약자로 제어의 반전을 의미합니다.

DI는 Dependency Injection의 약자로, 의존성 주입을 의미합니다..

개선되기 전 자동차의 설계도

class DuracellUltraBattery implements Battery{
	//대충 베터리에 대한 멤버변수 및 메서드
}

class Car{
	//대충 장난감 자동차에 필요한 재료들을 멤버변수로선언
    private Battery battery;//베터리
    
    public Car(){
   		//대충 다른 멤버변수들 초기화하는 코드
        battery = new DuracellUltraBattery();//베터리를 듀라셀 울트라 베터리 타입으로 할당
    }
	
    //대충 장난감 자동차가 동작하는 메서드
}

Car라는 클래스 속에서 직접 DuracellUltraBattery라는 베터리구현체를 직접 명시를 하며 할당해주고 있습니다. 이는 Car 클래스가 DuracellUltraBattery 클래스의 생명주기를 "제어"하고 있다고 보면됩니다.

이는 또한 Car라는 클래스와 DuracellUltraBattery 클래스의 결합도가 높다는것을 의미하기 때문에, 객체지향의 원칙에 알맞지 않은 코드가 됩니다.
따라서 위의 시나리오와 같이 베터리의 종류를 바꾸기 위해서 Car라는 클래스를 수정해야하는 불상사가 일어나게 됩니다.

개선된 후의 자동차 설계도

class Car{
	//대충 장난감 자동차에 필요한 재료들을 멤버변수로선언
    private Battery battery;//건전지 틀
    
    public Car(Battery battery){
   		//대충 다른 멤버변수들 초기화하는 코드
        this.battery = battery;//건전지 타입이기만 하다면 어떤 건전지든 상관없다!
    }
	
    //대충 장난감 자동차가 동작하는 메서드
}
public class Factory{
	public static void main(String[] args){
    	Battery bexel = new BexelBattery();//공장에서 Bexel 베터리를 준비해놓는다!
        while(true){//공장은 쉴틈없이 돌아가야죠
        	Car car = new Car(bexel);//마구마구 장난감을 찍으며, 건전지로는 Bexel을 넣는다
            
            //대충 포장하는 코드
            
            //대충 트럭으로 나르는 코드
        }
    }
}

Car라는 클래스 속에서 직접 베터리 타입을 명시하며 제어하는것이 아닌, 베터리 틀을 만들어서 생성자를 통해 베터리라는 인터페이스 혹은 클래스를 구현한 구현체를 "주입" 받아서 사용하는 코드입니다.
이는 Car라는 클래스가 Battery라는 클래스를 제어하는것이 아닌, Factory를 통해 Battery라는 클래스를 주입받기 때문에 제어권이 Car에서 Factory로 넘어간것을 확인할 수 있습니다.

이렇게 IOC는 제어권이 기존 클래스에서 Framework, Container, 혹은 Factory로 넘어가서 객체의 생명주기를 관리하는것같이 제어의 흐름이 반전되는것을 의미합니다.

또한 위의 예시에서는 Factory라는 클래스에서 Battery의 구현체를 직접 선택해서 Car라는 클래스의 배터리 틀(느슨한 결합) 생성자로 "주입"하고있습니다.
이런식으로 하나의 객체가 다른객체의 의존성을 주입시켜주는 방법을 DI라고 합니다.

그럼 왜 이런걸 쓰나요?

자바는 객체지향 언어입니다.
객체지향의 원칙중 아래와 같은 원칙이 존재합니다.

Open Closed Principle (개방-폐쇠 원칙)

이는 확장에는 열려있어야 하고, 변경에는 닫혀있어야 한다는 객체지향의 원칙중 하나인데요, 위의 예시로 들면 다음과 같습니다.

"듀라셀에서 건전지에 대한 성능 업그레이드를 하고 새로운 건전지를 출시해서 장난감 자동차에 끼워 넣는다고 했을 때, 장난감 자동차에 대한 설계도면을 바꿔야 할까요?"

답은 X입니다.
즉 확장(듀라셀 건전지의 업그레이드) 에 대해서는 열려있어야 하며, 변경(건전지에 대한 성능을 업그레이드 한다고 해서, 장난감 자동차의 설계도면을 바꿔야하나?) 에는 닫혀있어야 한다는 원칙입니다.

이를 IOC & DI를 활용한다면 주입받는 객체가 달라진다고 해도, 주입 당하는 객체의 입장에서는 느슨한 결합을 통해 주입받기 때문에 변경을 할 필요가 없어지기 때문에 OCP원칙을 잘 지킬 수 있다는 점입니다!

Single Responsiblity Policy (단일 책임 원칙)을 지킬 수 있습니다.

자동차는 과연 건전지의 종류를 알 필요가 있을까요?

답은 X입니다.
장난감 자동차는 그저 건전지라는 느슨한 결합을 통해 전원만 공급받을 수 있다면, 듀라셀이든 Bexel이든 상관없이 굴러갈 수 있게됩니다. 즉 장난감 자동차 안에 건전지의 종류라는 "책임"은 없다 입니다.
이런 IOC & DI를 이용하게 된다면 객체지향의 원칙을 지키며 개발을 해 나갈 수 있다는 장점이 있습니다.

profile
Hi🖐

2개의 댓글

comment-user-thumbnail
2023년 4월 17일

감동적인 글 잘 읽었습니다.

1개의 답글