💡 Chapter 11. 의존 관계 역전 원칙 (DIP)
의존 관계 역전 원칙(DIP) - Dependency-Inversion Principle
의존 관계 역전 원칙(DIP)
상위 수준의 모듈이 하위 수준의 모듈에 의존해서는 안 된다. 둘 다 추상화에 의존해야 한다.
또한 추상화는 구체적인 사항에 의존해서는 안 된다. 구체적인 사항이 추상화에 의존해야 한다.
기존의 병폐
- 상위 수준의 모듈이 하위 수준의 모듈에 의존하는 경우
- 상위 수준의 모듈은 어플리케이션의 본질을 담고 있다.
- 그러나 상위 모듈이 하위 모듈에 의존할 때, 하위 모듈 변경은 상위 모듈에 직접적인 영향을 미칠 수 있고, 나아가서 상위 수준 모듈이 변경되게 할 수도 있다.
- 상위 모듈은 어떤 식으로든 하위 모듈에 의존해서는 안 된다.
- 정책이 구체적인 것에 의존하는 경우
- 정책을 결정하는 것은 상위 수준의 모듈이다.
- 우리가 재사용하기를 원하는 것은 정책을 결정하는 상위 수준의 모듈이다.
- 상위 수준의 모듈이 하위 수준의 모듈에 독립적이면, 재사용하기 쉽다.
역전(inversion)
잘 설계된 객체 지향 프로그램의 의존성 구조는 전통적인 절차적 방법에 의해 일반적으로 만들어진 의존성 구조가 역전 된 것이다.
레이어 나누기
잘 구조화된 모든 객체 지향 아키텍처는 레이어를 분명하게 정의했다. 여기서 각 레이어는 잘 정의되고 제어되는 인터페이스를 통해 일관된 서비스의 집합을 제공한다. - 부치(Booch) -
미숙한 레이어
역전된 레이어
그래서 역전은?
의존성의 방향을 역전시키고, 하위 수준 모듈이 상위 수준 모듈에 선언된 인터페이스에 의존하게 만드는 것
여기서 역전은 의존성 뿐만 아니라, 인터페이스 소유권 에 대한 것도 의미한다.
헐리우드(Hollywood) 원칙이란?
- 하위 수준 모듈에서 시스템에 접속을 할 수는 있지만, 어떤 식으로 그 모듈을 사용할지는 상위 수준 모듈에서 결정하게 된다.
- 즉, 상위 수준 모듈에서 하위 수준 모듈에
먼저 연락하지 마세요. 저희(상위 수준 모듈)가 먼저 연락 드리겠습니다.
라고 말하는 원칙이다.
- DIP와 관계가 깊다.
이렇게 의존성을 역전시킴으로써, 우리는 좀 더 유연하고, 튼튼하고, 이동이 쉬운 구조를 만들 수 있다.
추상화에 의존하자
구체 클래스(concrete class)에 의존해서는 안 되고, 프로그램의 모든 관계는 어떤 추상 클래스나 인터페이스에 맺어져야 한다.
고지식한 원칙
- 어떤 변수도 구체 클래스에 대한 포인터나 참조값을 가져서 안 된다.
- 어떤 클래스도 구체 클래스에서 파생(상속)되어서는 안 된다.
- 어떤 메소드도 그 기반 클래스에서 구현된 메소드를 오버라이드해서는 안 된다
: LSP포함
구체 클래스가 너무 많이 변경 되지 않으면서, 다른 비슷한 파생 클래스가 만들어지지 않는다면 이것에 의존하는 것은 그리 큰 해가 되지 않는다
→ 휘발적인 클래스에는 직접적으로 의존하지 않아야 한다.
너무 고지식할 필요까진 없다.
→ 상황에 따라서 고지식할 때도 있고, 유연하게 넘겨야할 때도 있다.
결론
- 전통적인 절차 지향 방식은 정책이 구체적인 것에 의존하는 의존성 구조를 만든다. 이 경우 정책이 구체적인 것의 변경에 따라 같이 변한다.
- 객체 지향 프로그래밍은 이것을 역전 시켜서 구체적인 것과 정책이 모두 추상화에 의존하게 한다.
- 프로그램의 의존성이 역전되어 있다면 이것은 객체 지향 설계이며, 의존성이 역전되어 있지 않다면 절차적 설계이다.
- 의존성 역전을 사용하면 프레임워크가 재사용 가능해지고, 변경에 탄력적이며, 추상화와 구체적 사항이 서로 분리되어 있기 때문에, 이 코드는 유지보수하기가 훨씬 쉽다.
💡 Chapter 12. 인터페이스 분리 원칙 (ISP)
인터페이스 분리 원칙(ISP) - Interface-Segregation Principle
- ISP는 ‘비대한’ 인터페이스의 단점을 해결한다.
- 비대한 인터페이스는 응집력이 없는 인터페이스이다.
인터페이스 오염
- 클라이언트가 사용하지 않는 인터페이스가 구현되어 있는 경우
- 일부 구상체에 불필요한 인터페이스가 구현되어 있는 경우
- 파생 클래스가 새로운 메소드를 필요로 할 때마다 그 메소드가 기반 클래스에도 포함되어야 한다. 이것은 기반 클래스의 인터페이스를 ‘비대하게’ 만든다
→ 불필요한 복잡성 과 불필요한 중복성
클라이언트가 인터페이스에 미치는 반대 작용
- 클라이언트의 요구사항 변경으로 인해 인터페이스가 변경되는 경우가 자주 생긴다.
- 이 상황에서 해당 인터페이스가 비대해서 여러 구상체를 지니고 있으면, 요구사항 변경으로 인한 영향 범위가 넓다.
- 그로 인해서 비용과 부작용의 위험성이 급격하게 증가한다.
인터페이스 분리 원칙(ISP)
클라이언트가 자신이 사용하지 않는 메소드에 의존하도록 되어서는 안 된다.
어떤 클라이언트(A)가 자신은 사용하지 않지만 다른 클라이언트(B)가 사용하는 메소드를 포함하는 클래스에 의존할 때, 그 클라이언트(A)는 다른 클라이언트(B)가 그 클래스에 가하는 변경에 영향을 받게 된다.
우리는 가능하다면 이런 결합을 막고 싶다. 따라서 인터페이스를 분리 하기를 원한다.
다중 상속을 통한 분리
위 구조처럼 다중 상속을 통해 인터페이스를 분리할 경우, 기반 클래스들은 분리된 인터페이스를 통해 같은 객체를 사용하게 된다. 이는 다른 클라이언트의 변경에 의한 영향을 피할 수 있다.
복합체와 단일체
만약 인터페이스 복수 개(복합체)와 클래스 1개(단일체) 중 선택해야 할 경우에는 인터페이스 복수 개(복합체)인 방식으로 구현하라.
결론
- 비대한 클래스는 클라이언트간의 높은 결합도를 유발한다.
- 클라이언트는 자신이 사용하는 메소드에만 의존해야 하는데, 비대한 클래스에 변경을 가하면 모든 나머지 클래스가 영향을 받게 된다
→ 클라이언트 고유의(client-specific) 인터페이스 여러 개로 분해해야 한다.
→ 이렇게 하면 호출하지 않는 메소드에 대한 클라이언트의 의존성을 끊고, 클라이언트가 서로에 대해 독립적이 되게 만들 수 있다