> For the complete documentation index, see [llms.txt](https://kickbell.gitbook.io/blog/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://kickbell.gitbook.io/blog/undefined-1/solid/dip.md).

# DIP, 의존성 역전 원칙

이 글은 [베어코드](https://www.youtube.com/channel/UCEuyt4RB4mlMfADQMz-d2DQ) 님의 [Swift Object Oriented Progoramming](https://youtube.com/playlist?list=PLoPKxuu4_dG3jCiMcbskRuNsgZsdZ4zz6) 을 보고 정리한 글 입니다.

* 코드에서 통상적으로 발견되는 특별한 문제를 발견
  * 상위 수준의 모듈이 하위 수준의 모듈에 의존성을 가지는 경향
  * 정책이 구체적인 것에 의존하는 경향
* DIP의 목적
  * 모듈간의 의존관계를 끊는 방법 제시
  * 변경이 다른 코드에 영향을 최소화 시킬 방법 제시

#### 모듈의 의존관계

* 코드는 어떻게든 의존관계를 가지고 작성된다.
* 의존관계 자체를 없앨 수는 없다.
* 의존관계를 잘 정리하지 않으면 코드는 경직성을 띄게 된다. (기능의 추가/수정이 까다로워짐)
* 의존관계가 존재할 수 밖에 없다는 전제하에 무엇이 무엇에 의존하느냐에 따라 코드가 좋아질 수 있다.

#### 서로 의존관계를 가지는 모듈

* 생각보다 자주 볼 수 있는 형태
* 의존관계는 단방향으로 흘러가는 것이 좋음
* 이렇게 서로 의존관계를 가지는 모듈이면 weak을 사용해야 한다고 이미 알고 있을 것이다. ![](https://images.velog.io/images/dev_kickbell/post/fe233830-8387-4182-8b25-fab9daec33dc/image.png)

#### 의존관계가 cycle을 만드는 경우

* 어느 클래스도 독립적으로 사용될 수 없다. 최악임. ![](https://images.velog.io/images/dev_kickbell/post/f6061271-4340-481a-a605-709828a7441a/image.png)

#### 단방향으로 흘러가는 의존관계

* A는 B에 의존하고 있고, B는 C에 의존하고 있다. 이런 경우 C는 A,B에 의존하지 않기 때문에 독자적으로 사용할 수 있는 클래스이다. B도 C에 의존하고있지만 A에는 의존하지 않는다.
* 모든 클래스들이 의존관계가 없다면 최선이지만 현실적으로 어렵다.
* 되도록이면 한방향으로 의존관계가 흘러가는 것이 큰 도움이 됨. ![](https://images.velog.io/images/dev_kickbell/post/958a3438-a511-431f-a255-e33b030107ce/image.png)
* 만일 C가 A혹은 B에 의존해야 하는 상황이 있다면? 그니까 직접적으로 A, B를 참조해서 쓰면 안된다는 것이다.
* 이럴때는 DIP가 해결책이 될 수 있다. ![](https://images.velog.io/images/dev_kickbell/post/6b955079-1ca4-4a02-ad14-bbc6f7b86303/image.png)

#### 의존의 방향

* 의존관계는 어떤 방향으로 흘러가야 할까?
  * 구체적인 부분 -> 추상적인 방향으로 의존관계를 가져야함.
  * 추상적인 것이 구체적인 것에 의존하지 않아야 한다.
* 정책이 구체적인 것에 의존하는 경향 ( 정책은 추상적인 것 )
* 상위수준은 하위수준에 의존하면 안된다.
  * 상위수준 : 추상적인 부분
  * 하위수준 : 구체적인 부분

#### iOS의 대표적인 예시 1 - UITableView

* UITableView : 추상적
* UITableView를 이용해서 구현된 코드 및 Cell : 구체적
* 무슨말이냐면, Swift에 구현되어있는 아래의 UITableView class를 보면 UITableView는 개발자가 커스텀한 Cell에 직접적으로 의존하지 않는다.(의존없음) 리턴값으로 커스텀셀 대신 Cell의 추상레벨인 UITableViewCell을 사용한다.

```swift
// Used by the delegate to acquire an already allocated cell, in lieu of allocating a new one.
open func dequeueReusableCell(withIdentifier identifier: String) -> UITableViewCell? 
```

* 동작도 마찬가지다. 동작에 관한 부분은 UITableViewDatasource, UITableViewDelegate에 위임을 하게 된다. UITableView 입장에서는 2개의 프로토콜(datasource, delegateP)과 UITableViewCell 만으로 동작을 하게 되는 것이다.
* 즉, 직접적으로 의존하지 않고 인터페이스에 의해 의존함.

#### iOS의 대표적인 예시 2

* UIViewController와 내가 만든 클래스인 MyViewController가 있다.
* UIViewController는 view life cycle에 대한 정책을 결정한다. (뷰디드로드, 뷰윌어피어...)
* 그리고 MyViewController는 내가 커스텀으로 구현한 구체적인 코드가 있다.
* 즉, MyViewController는 UIViewController에 의존하지만 반대로 UIViewController는 MyViewController에 의존하고 있지 않다. MyViewController가 존재하는지도 모른다.

#### 정리

* 소프트웨어 모듈들을 분리하는 원칙. 상위계층(정책결정)이 하위계층(세부사항)에 의존하는 전통적인 의존관계를 반전(역전)시켜 상위계층이 하위계층의 구현으로부터 독립되게 한다.

> 뭔말이냐면, 만약에 UITableView, UITableViewCell 클래스가 없었다고 치고 같은 기능을 구현한다고 했으면 그 테이블뷰를 커스텀셀에 의존되게 만들 가능성이 크다는 말이다. 그 말은 재사용이 불가능하게 설계를 했을 거라는 말이다. 위에서 UITableView가 리턴값으로 커스텀셀이아니라 UITableViewCell을 사용하는 것과 같은 맥락인 것 같다. 그렇게 함으로써 재사용이 가능해지고 의존하지 않게 분리하고 있으니까.

* 상위모듈은 하위모듈에 의존해서는 안된다. 상위모듈과 하위모듈 모두 추상화에 의존해야 한다.
* 추상화는 세부사항에 의존해서는 안된다. 세부사항이 추상화에 의존해야 한다.

#### 더 깊게 생각해보기

* 꼭 protocol을 사용하는 것만이 DIP를 사용하는 것이라고 정의되어 있지 않음. 그래서 이렇게도 생각해볼 수 있다.
* superclass를 상속받아 subclass에서 사용할때 superclass의 기능을 확장하기 위해서 많이 쓰는데 사실 그것은 좋은 방법이 아니다. 오히려 subclass를 superclass의 구체화라는 개념으로 사용하는 것이 더 좋다.
* 이 말은 위에서 계속 설명한것처럼 superclass는 추상적인 부분, subclass는 구체적인 부분이해라고 이해할 수 있다.
* superclass의 추상적인 로직은 subclass에 의존하지 않고 작성되어야 한다.
  * 세부사항(구체화)에 의존되지 않음.
  * LSP와 개념적으로 유사한 부분이 있음.
* 구체화를 위해서 subclass는 superclass의 메소드를 override함. (뷰라이프싸이클 같은것처럼)

> 계속 같은 말을 하고 있다. 테이블뷰처럼. 커스텀셀이 뭐하든 말든 테이블뷰는 몰라야 된다. 마이뷰컨에서 뷰디드로드에 뭐하든 말든 UIViewContoller가 알게 뭐겠는가? 안그런가? 가끔 superclass가 subclass가 뭐하는지 알고있는 경우?에 코드가 꼬인경우인데(쌍방의존관계라고나할까?)그건 잘못된 설계이다.

#### hollywood할리우드 원칙

* "영화에 출연하고 싶으면 연락처를 주고 가세요 (먼저 연락하지 마세요), 필요하면 저희가 연락드리겠습니다."
* 배우(구체적인쪽)은 영화사(추상적인쪽)에게 연락하지말고 연락처만 주고가라는거다. 영화사는 그저 필요하면 부르면 된다.

#### 다시정리 테이블뷰, 마이뷰컨트롤러

1. UITableView (프로토콜)
   * 구체화된쪽(내가 구현한쪽, VC)에서 delegate, datasource를 = self로 테이블뷰에다가 등록해놓으면(연락처를 남긴다) 추상화 된쪽(테이블뷰)이 필요할 때 콜백해서 지금 테이블뷰 그려라\~!하면 이제 내가 구현해놓은 커스텀 delegate, datasource가 실행되면서 셀을 그리는거다.
   * 결국 의존관계를 역전시켜서 통상 추상적인 부분이 구체적인 부분에 참조했는데 그걸 반대로 구체적인 부분이 추상적인 부분을 참조하도록 두 모듈의 의존관계를 `단방향`으로 만들어서 만들어놓은 `UITableView`를 재활용할 수 있게 만든 것이다.
2. MyViewController(상속)
   * 프로토콜사용이 아니라 상속하는 경우에도 마찬가지다. 추상적인 쪽(UIViewContoller)는 구체적인 쪽(MyViewController)가 뭐하는지 모르고 알 것도 없다. 대신에 이제 view life-cycle을 관리하는 UIViewContoller가 필요할 때 MyViewController한테 알려주면, MyViewController는 viewDidLoad같은 함수를 override해서 자기가 원하는대로 코드를 구체화 해주는 것이다.
   * 즉 MyViewController는 UIViewContoller를 참조하지만, UIViewContoller는 MyViewController를 참조하지 않는다.
   * 그렇게 함으로써 UIViewContoller는 다른곳에서도 재활용이 가능하다.

#### 의존관계를 역전시켜서 얻을 수 있는 것

* 두 모듈간의 의존관계를 단방향으로 만들어준다.
* 추상화된 부분의 코드는 재활용성을 증가시킬 수 있다.

#### 프로젝트에 적용

* 지금까지는 테이블뷰나 뷰컨트롤러를 통해서 이미 정의되어있는 코드를 이야기했기 때문에 DIP를 몰라도 DIP를 사용하고 있었다. 근데 이제는 우리가 만들어야 되니까 더 이야기해본다.
* 무엇이 추상적인 개념이고 구체적인 개념인지를 정의해야 한다.
* 추상적인 부분(정책)
  * 프로젝트에서 자주 변경되지 않을 부분을 추상적인 부분으로 정의한다.
  * 해당 로직의 뼈대라고 볼 수 있다.
* 구체적인 부분(세부)
  * 자주 변경되는 부분을 구체적인 부분으로 지정한다.
  * 자꾸 추가되는 부분도 구체적인 부분으로 지정한다.

#### 의존성의 이행

* 의존성의 이행이라 함은 그런것이다. A를 쓰려고했는데 B가딸려오고, B에있는 C까지 줄줄이 딸려오는 상황.
* 의존성의 이행은 스파게티 코드를 만든다.
* 적합한 위치에서 DIP를 사용하면 의존성 이행을 막을 수 있다.
* 이런 경우에는 인터페이스를 제공하는 쪽은 상위레이어(추상화, 정책, 클라이언트)이다.
* 상위레이어는 인터페이스만으로 코딩을 하며, 하위레이어에서는 그 인터페이스로 구현한 클래스를 구체화 하는식으로 만든다.
* 그러니 상위레이어는 변화가 없어야 한다. 또 상위레이어는 구체화에 의존되어 있지 않기 때문에 재활용이 쉬워진다. 상위레이어의 내용의 변경 역시 국지적(일정에 지역에 한정된것, 여기서는 코드가 변경되더라도 상위레이어 1개에서만 코드가 변경된다는 의미로 통하는듯)으로 이루어지니 테스트하기가 쉬워진다.
* 하위레이어(구체화된레이어)는 수시로 변경된다. 기능이 변하면 또 변경되고 그런다. 다만 이 변경이 다른 부분에 영향을 최대한 주지않게 설계되었기 때문에 기존에 작성된 (DIP적용안하는) 방식보다 더 변경에 유리하다.

#### 인터페이스의 소유

* 인터페이스(Swift에서는 Protocol)는 누가 소유(제공)하는가 ?
* 클래스 A와 B가 있다. 그리고 클래스 A가 B를 가지고 있다면 ?
  1. 클래스 A가 인터페이스를 제공하고 클래스 B가 사용하는게 맞을까?
  2. 클래스 B가 인터페이스를 제공하고 클래스 A가 사용하는게 맞을까?
* 정답은 2번이다. 한 클래스(A)가 다른 클래스(B)를 직접적으로 참조한다면(갖고있다면) 반대쪽 클래스(B)가 인터페이스를 제공해서 의존관계를 역전시킨다.

#### UITableViewController

* 우리는 통상적으로 유틸리티 라이브러리(코코아팟?)이 인터페이스를 처리하는 것에 익숙하다.
* DIP를 적용하면 클라이언트(사용하는 쪽)가 추상인터페이스를 사용하고 서버(사용되는 쪽)가 그것에서 파생되어 나온다. (이건 예전에 공부할때 그.. 프로토콜정의해갖고 라이브러리로 제공되는 애들의 추상인터페이스를 갖고와갖고.. 나누게하는 뭐그런건가?애매쓰..)
* 그렇다면 UITableViewController와 UITableView의 관계에서 누가 클라이언트고 누가 서버일까?
* 헷갈린다.
* 정답은 UITableViewController가 서버고 UITableView가 클라이언트이다.
* 쉽게생각하면 UITableView가 테이블뷰의 기능을 제공하는 서버고 그것을 사용하는 UITableViewController가 클라이언트처럼 보인다. 여기서는 기능의 제공이 문제가 아니라 구체화된 내용을 누가 제공하냐에 따라 달라진다.
* 구체화된 내용을 제공하는 쪽이 서버이고, 그리고 그것을 사용하는 쪽이 클라이언트이다.
* 이 부분은 눈에 보이는 게 다가 아니라 이해를 해야 알 수 있는 부분들이다.

#### 클라이언트가 인터페이스를 소유하는 이유

* 인터페이스의 변경은 클라이언트의 요구에 의해서만 발생하기 때문이다.
* 변경을 유발한 쪽에서 인터페이스를 가지고 있는 것이 유리하다.

#### DIP의 핵심주장 : 추상화에 의존하라.

* 어떤 변수로 구체 클래스에 대한 참조를 가지지 말라.
* 어떤 클래스도 구체 클래스에서 파생하지 말라.
* 어떤 메소드도 기반 클래스에서 구현된 메소드를 오버라이드 하지 말라.
* 쉽지않다.. 모든 경우에 이룰을 적용하는것은 어렵다.

#### 클래스의 휘발성에 따른 적용

* 그래서 다행히 비휘발적인 클래스의 경우 강하게 DIP를 적용할 필요가 없다.
* 비휘발적인 클래스에 의존하는 것은 큰 해가 되지 않기 때문이다.
* 여기서 비휘발성인 클래스란 ?
  * 인터페이스와 로직이 변경이 거의 없는 클래스를 말한다.
  * SDK에서 제공되는 것들 String, Data같은 자료구조나 클래스들을 말한다. (String의 인터페이스가 바뀔 것을 우려해서 그것을 래핑해서 사용하진 않는다.)
* 추상화된 클래스는 비휘발적인 클래스에 가까움. 잘변하지 않기 때문에 추상화된 클래스에 의존하는 것이 더 낫다는 거다.

#### 예시: 버튼과 램프

1. 버튼이 램프를 제어
   * 버튼이 램프를 가지고 있음
   * 이 버튼은 램프에만 사용할 수 있음

> 버튼 클래스가 램프 클래스를 갖고있는 상황인거지. 그래서 버튼을 ON 해주면 버튼 안에있는 램프가 켜지는 상황이다.

1. 버튼이 인터페이스(프로토콜)를 제공하고 그것을 이용해서 램프를 제어
   * DIP 적용
   * 버튼은 이제 램프가 아니어도 버튼이 있는 다양한 다른 것들에 사용가능

> 뭔소린지 아직 모르겠음.

1. 버튼, 램프 모두 소유권이 없는 인터페이스 (ex: SwitchableDevice) 사용
   * 버튼과 램프는 인터페이스에만 의존성을 갖게 됨.

> 뭔소린지 아직 모르겠음.
