반응형

의존성 다루기

 

IoC -> Inversion of Control (제어의 역전)

DI -> Dependency Injection (의존성 주입)

DIP -> Dependency Inversion principle (의존관계 역전 법칙)

 

 

Dependency

A -depends on(방향성이 존재)-> B

A는 B에 의존한다.

= B가 변경될 때 A가 영향을 받을 수 있다.

 

 

A가 B를 사용한다는 의미

소스코드에서 생각해본다면?

  • 멤버변수
  • 메서드 파라미터
  • 메서드 내 로컬 변수로 사용
  • 메서드의 리턴 값으로 사용

 

 

Class 사이의 관계

  • Association - 연관 관계
  • Dependency - 의존 관계
  • Generalization - 일반화 관계
  • Realization - 실체화 관계

 

 


Association - 연관 관계

 A  -b ⎯⎯⎯  B 

 

객체 LifeCycle 차이

class A {
	private B b;
    
    public (B b) {
    	this.b = b;
    }
} //Aggregation 집합관계

 A  ◇⎯⎯⎯  B 

 

class A {
	private B b;
    
    public () {
    	this.b = new B();
    }
} //Composition 복합관계

 A  ◆⎯⎯⎯  B 

 

 

Directed Association - 연관 관계

 A  -b ⎯⎯⎯>  B 

 

 


Dependency - 의존 관계

 A  - - - - - ->  B 

class A {
	D foo(B b) {
    	D d = b.bar(new C())
        ...
        return d;
    }
}

 

 


Generalization - 일반화 관계 (상속)

 A   ⎯⎯⎯▷  B 

class A extends B {
	...
}

 

 

 

 


Realization - 실체화 관계

 A   - - - - -▷  B 

class A implements B {
	...
}

 

 

 


IoC

Inversion of Control

 

  • 1988년  Ralph E. Johnson & Brian Foore 가 작성한 논문에 처음으로 등장
  • 1993년 Richard Sweet 의 논문과 1994년 발간된 Gof Design Pattern 에서 헐리우드 법칙(Hollywood Principle)이란 용어로 새롭게 언급됨
  • Gof Design Patterns
  • Chapter 5 행동패턴 - 템플릿 메서드(Template method)에 등장
  • 템플릿 메서드는 "헐리우드 원칙(Hollywood principle)" 역전된 제어구조를 끌어냅니다.
  • 위 본문의 주석에 아래와 같은 내용이 쓰여있음
  • 1960년대 미국에서 면접관들이 쓰기 시작한 말인데, 나중에 극장가에서 배우들의 오디션을 보고 거절할 때 더 많이 써 유명해졌다.

 

 

헐리우드 법칙(Hollywood Principle)

"Don't call us, we'll call you."

"전화하지 마세요, 우리가 연락 할게요."

 

 

헐리우드 법칙을 소프트웨어에 적용하면?

"프로그램의 흐름 제어의 주체가 바뀌는 것"

"호출하지 마세요, 우리가 호출 할게요."

 

 

 

전통적인 방식의 프로그램 제어 흐름

  • 개발자가 작성한 코드가 메인함수부터 순차적으로 실행된다.
#include <stdio.h>

ind add (int x, int y) {
	return x + y;
}

void main() {
	int result = add(10, 20);
    prinf("add : %s", result);
}

 

 

 

제어 흐름이 역전되었을 때 프로그램 흐름

  • 메인함수가 실행된 이후, 개발자가 작성한 코드가 언젠가 호출 당한다.
@SpringBootApplication
pulic class DemoApplication {

	public static void main(String[] args) {
    	SpringApplication.run(
        	DemoApplication.class, args);
        }
    } 
 }
 //SpringBoot의 Helloworld

 

namespace HelloWorld {
	public partial class MainWindow : Window {
    	public MainWindow() {
        	InitializeComponent();
        }
        
        private void Botton_Click(object sender, RoutedEventArgs e) {
        	label.Content = "Hello, world!";
        }
    }
}
//WPF(.net)의 HelloWorld
  • 1. main 함수 실행
  • 2. application 로딩
  • 3. 버튼 틀릭
  • 4. 등록된 핸들러 실행
  • 5. 동작 실행

 

 

Callbacks, schedulers, event loops, dependency injection, and the template method are examples of design patterns that follow the inversion of control principle, although the therm is most commonly used in the context of object-oriented programming. - wikipedia

 

 

 


DI

Dependency Injection

 

  • 2004년 Martin Fowler가 아티클에서 처름으로 언급
  • Inversion of Control is too generic a term, and thus people find it confusing. As a result with a lot of discussion with various IoC advocates we settled on the name Dependency Injection. - Martin Fowler
  • IoC는 너무 범용적인 용어라 사람들이 혼란스러워 DI 이름을 만들어냈다고 함.

 

 

갑자기 왜 새로운 용어를 만들었지?

DI를 설명하기 위해 샘플 예제를 보여줌

interface MovieFinder {
	List<Movie> findAll();
}

class MovieLister {
	private final MovieFinder finder;
    
    public MovieLister() {
    	this.finder = new ColonDelimitedMovieFinder("movies1.txt");
    }
    
    public List<Movie> moviesDirectedBy(String director) {
    	return finder.findAll()
        	.stream()
                .filter(it -> it.isSameDirector(director))
                .collect(toList());
    }
}

 

인터페이스에만 의존(컴파일타임 의존성)하고, 구현 클래스에는 의존하지 않고 싶음(런타임 의존성)

 

마틴 파울러는 "엔터프라이즈 애플리케이션 아키텍쳐 패턴" 에서 이를 Plugin pattern으로 정의함

플러그인의 구현체를 어떻게 조립해서 어플리케이션에 밀어넣는 것이 핵심 문제가 된다.

 

class MovieLister {
	private final MovieFinder finder;
    
    public MovieLister(MovieFinder finder) {
    	this.finder = finder;
    }
    
    public List<Movie> moviesDirectedBy(String director) {
    	return finder.findAll()
        	.stream()
                .filter(it -> it.isSameDirector(director))
                .collect(toList());
    }
}

ApplicationContext(BeanFactory)

-> MovieFinder 구현체에서 찾아서 MoiveLister를 생성하여 런타입 의존성 연결

 

 

결국, 경량 컨테이너(ex. Spring)들에서 IoC를 이용하여

별도의 조립모듈에서 객체를 만들고, 객체간 디펜던시를 연결하여 관리함

때문에, IoC Container 혹은 DI Container 라고도 부름

 

"The basic idea of the Dependency Injection is to have a separate object, an assembler" - Martin Fowler

디펜던시 인젠션의 기본 아이디어는 객체들을 연결해주는 별도의 객체를 갖는 것이다.

 

 

인터페이스에만 의존하고, 구현 클래스에는 의존하지 않음

(의존성 주입보다는 의존관계 주입이 맞는 표현)

 

 

 


DIP

Dependency Inversion principle

 

  • 1996년 Martin, Robert C 이 DIP 아티클을 발표하였으며, 두가지 원칙을 제시함
  • A. High-level modules should not depend upon low-level modules. Both should depend on abstractions.
  • 상위 레벨 모듈은 하위 레벨 모듈에 의존하면 안되고, 둘 다 추상적인 것에 의존해야 한다.
  • B. Abstractions should not depend upon details. Details should depend upon abstractions.
  • 추상적인 것은 구체적인 것에 의존하면 안되고, 구제적인 것이 추상적인 것에 의존해야 한다.

 

class MovieLister {
	private final MovieFinder finder;
    
    public MovieLister() {
    	this.finder = new ColonDelimitedMovieFinder("movies1.txt");
    } //DIP 위반
...

▲ 추상적인 개념의 구체적인 개념의 의존하여 DIP 위반

 

class MovieLister {
	private final MovieFinder finder;
    
    public MovieLister(MovieFinder finder) {
    	this.finder = finder;
    } //추상적인 것에 의존하도록 변경
...

 

 

MovieLister ⎯⎯⎯▷ ColonDelimitedMovieFinder

컴파일타임 의존성런타임 의존성의 방향이 같다.

 

MovieLister ⎯⎯⎯▷ <<interface>>MovieFinder

MovieFinderImpl ⎯⎯⎯▷ <<interface>>MovieFinder

MovieLister ⎯⎯⎯▷ ModieFinderImpl

컴파일타임 의존성 런타임 의존성의 방향이 다르다.

 

 

DIP를 적용하게되면 컴파일 타임의 의존성은 코드의 의존성과 같다.

때문에 각 패키지를 독립적으로 빌드가 가능하다.

 

 


정리

IoC -> 프로그램의 흐름 제어의 주체가 바뀌었는가?

DI -> 객체간의 의존관계를 어떻게 주입할 것인가?

DIP -> 추상적인 것에 의존하고 있는가?

 

 


반응형

+ Recent posts