DI와 DIP

이강용·2024년 2월 28일
0

CS

목록 보기
21/109

의존성주입과 의존관계 역전 원칙

의존성 주입(DI,Dependency Injection)

  • 객체 지향 프로그래밍에서 한 객체가 다른 객체의 의존성(즉, 필요로 하는 객체나 서비스)을 외부에서 제공받는 디자인 패턴. 의존성 주입을 사용하면 객체는 자신이 의존하는 객체를 직접 생성하지 않고, 외부로부터 주입받게 된다. 이로써 객체 간의 결합도가 낮아지며, 코드의 재사용성과 테스트 용이성이 향상된다.

"의존한다"라는 말의 의미는?

A가 B에 의존한다. = B가 변하면 A에 영향을 미치는 관계 = A → B를 의미한다.

의존한다의 자바 코드 예시

자바 - Project의 DI를 적용하지 않은 예시

package programmers;


class BackendDeveloper {
	public void writeJava() {
		System.out.println("자바가 좋아");
	}
}

class FrontendDeveloper {
	public void writeJavascript() {
		System.out.println("자바스크립트가 좋아");
	}
}



public class Project {
	
	private final BackendDeveloper backendDeveloper;
	private final FrontendDeveloper frontendDeveloper;
	
	public Project(BackendDeveloper backendDeveloper, FrontendDeveloper frontendDeveloper) {
		this.backendDeveloper = backendDeveloper;
		this.frontendDeveloper = frontendDeveloper;
	}	
	
	public void implement() {
		backendDeveloper.writeJava();
		frontendDeveloper.writeJavascript();
	}
	
	
	
	public static void main(String[] args) {
		Project a = new Project(new BackendDeveloper(), new FrontendDeveloper());
		a.implement();
	}
}

자바 - Project의 DI 적용한 예시

  • 의존적인 화살표가 역전 된것을 볼 수 있으며, DI를 하게 되면 의존관계 역전 원칙이 적용된다.
package programmers;

import java.util.ArrayList;
import java.util.List;

interface Developer {
	void develop();
}


class BackendDeveloper implements Developer {
	
	@Override
	public void develop() {
		writeJava();
		
	}
	public void writeJava() {
		System.out.println("자바가 좋아 - new");
	}
}

class FrontendDeveloper implements Developer {
	
	@Override
	public void develop() {
		writeJavascript();
	}
	public void writeJavascript() {
		System.out.println("자바스크립트가 좋아 - new");
	}
}



public class Project {
	
	private final List<Developer> developers;
	
	public Project(List<Developer> developers) {
		this.developers = developers;
	}	
	
	public void implement() {
		developers.forEach(Developer :: develop);
	}
	
	
	
	public static void main(String[] args) {
		List<Developer> dev = new ArrayList<>();
		dev.add(new BackendDeveloper());
		dev.add(new FrontendDeveloper());
		Project a  = new Project(dev);
		a.implement();
	}
}

예를 들어, Car 객체가 Engine 객체에 의존하는 경우, Car객체는 Engine 객체를 직접 생성하지 않고, 생성자, 메서드 또는 프로퍼티(Setter)를 통해 외부로부터 Engine 객체를 주입 받을 수 있다.

DI 예시 2

package programmers;


interface Engine {
	void start();
}

class GasolineEngine implements Engine {
	@Override
	public void start() {
		System.out.println("가솔린 엔진이 시작됩니다.");
		
	}
}

class Car {
	private Engine engine;
	
	public Car(Engine engine) {
		this.engine = engine;
	}
	
	public void start() {
		engine.start();
		System.out.println("차량이 출발합니다.");
	}
}

public class Main {
	public static void main(String[] args) {
		
		//GasolineEngine 객체를 생성하고, 이를 Car 객체에 주입
		Engine engine = new GasolineEngine();
		Car car = new Car(engine);
		
		// Car 객체를 사용
		car.start();
	}

}
  • 위 로직은 의존성 주입(Dependency Injection)을 활용하여 Car객체가 Engine 객체에 의존하도록 설계한 예시이다. 여기서 Car 클래스는 Engine 인터페이스의 구현체를 필요로 하며, 구체적인 Engine구현체는 외부에서 Car에 주입된다.
  1. 인터페이스 정의 : Engine인터페이스를 정의하여, Car객체가 엔진을 시작하는 기능(start 메서드)에 대한 계약을 명시한다. 이 인터페이스는 Car와 엔진 구현체 사이의 결합도를 낮춰준다.
  2. 구현체 제공 : GasolineEngine 클래스는 Engine 인터페이스를 구현한다. 이 클래스는 Enginestart 메서드를 오버라이드하여 "가솔린 엔진이 시작됩니다."라는 메시지를 출력하는 구체적인 로직을 제공한다.
  3. 의존성 주입 : Main 클래스의 main 메서드에서 GasolineEngine 객체를 생성하고 이 객체를 Car객체의 생성자를 통해 주입한다. 이때 Car객체는 Engine 인터페이스에 의존하고 있으며, 구체적인 Engine 구현체는 생성 시점에 결정된다.
  4. 의존성 사용 : Car객체의 start메서드를 호출하면 내부적으로 주입받은 Engine객체의 start메서드를 호출한다. 이로 인해 GasolineEnginestart메서드가 실행되어 "가솔린 엔진이 시작됩니다." 메시지가 출력된다. 이후 "차량이 출발합니다." 메시지가 출력되어 Car 객체의 작동을 나타낸다.

의존관계 역전 원칙(DIP, Dependency Inversion Principle)

  • 의존관계 역전 원칙은 SOLID원칙 중 하나로, 고수준 모듈이 저수준 모듈에 의존하지 않도록 하고, 둘 다 추상화에 의존하게 만드는 원칙이다. 여기서 고수준 모듈이란 응용 프로그램의 핵심 비지니스 로직을 담당하는 부분저수준 모듈이란 데이터베이스 접근, 네트워크 통신과 같은 세부적인 기능을 담당하는 부분을 의미한다.

  • DIP를 적용하면, 구체적인 클래스 간의 직접적인 의존성이 제거되고, 인터페이스나 추상 클래스를 통해 의존성이 관리된다. 이로써 변경에 대한 유연성이 증가하고, 모듈 간의 결합도가 낮아지며, 코드의 재사용성과 유지보수성이 향상된다.

  • 의존관계 역전의 원칙은 아래와 같이 두 가지의 규칙을 지키는 상태를 말한다.

    • 상위 모듈은 하위 모듈에 의존해서는 안된다. 둘 다 추상화에 의존해야 한다.
    • 추상화는 세부사항에 의존해서는 안된다. 세부 사항은 추상화에 따라 달라져야 한다.

의존성 주입의 장 ・ 단점

장점단점
느슨한 결합 (Loose Coupling)초기 구성의 복잡성
유닛 테스트 용이성러닝 커브
코드 재사용 용이성오버 엔지니어링의 위험
프로그램의 확장성 향상DI 컨테이너에 대한 의존성
설정 및 부품 교체의 용이성비용 증대
속성 주입이 런타임에 일어나 컴파일 시점에 종속성 주입 에러를 잡기 어려움
profile
HW + SW = 1

0개의 댓글