: 컴포넌트 사이의 관계를 설명하는 원칙들
: 개발 가능성과 논리적 설계 사이의 균형을 다룬다. 컴포넌트 구조와 관련된 아키텍처를 침범하는 힘은 기술적이며 정치적이고 가변적이다.
ADP: 의존성 비순환 원칙
컴포넌트 의존성 그래프에 순환이 존재해서는 안 된다.
: 무언가를 작동하게 만들어 놓고 퇴근했을때, 누군가가 당신이 만든 것이 의존하고 있던 무언가를 수정한다면 작동하지 않게 될 것이다. 이를 숙취 증후군이라 부른다.
: 이는 많은 개발자들이 동일한 소스 파일을 수정하는 환경에서 발생한다, 소수의 개발자로 구성된 상대적으로 작은 프로젝트에서는 이 증후군이 그다지 큰 문제가 되지 않는다.
: 이를 해결하기 위한 두 가지 방법이 제시되었는데, 하나는 주 단위 빌드, 하나는 의존성 비순환 원칙이다.
•
주 단위 빌드
: 중간 규모의 프로젝트에서 흔하게 사용되는 방법으로 먼저 모든 개발자는 일주일의 첫 4일동안은 서로를 신경쓰지 않는다.
: 그런 후 금요일이 되면 변경된 코드를 모두 통합하여 시스템을 빌드한다. 이는 5일 중 4일을 개발자가 통합에 대한 신경을 쓰지 않게 해주는 장점을 가지지만
: 프로젝트의 규모가 점점 커짐에따라 금요일만으로는 통합이 불가능해지는 상황이 발생한다.
•
순환 의존성 제거하기
: 이 문제의 해결책은 개발 환경을 릴리즈 가능한 컴포넌트 단위로 분리하는 것이다.
: 자신이 의존하고 있는 컴포넌트의 릴리즈 버전만을 신경쓰면 되며, 릴리즈 버전이 업데이트 되더라도 언제 적용시킬지 고민하기만 하면 된다.
: 비순환 방향 그래프 ⇒ 어느 컴포넌트에서 시작하더라도 의존성 관계를 따라가면 최초의 컴포넌트로 돌아갈수 없다.
: 순환 방향 그래프 ⇒ Database를 릴리즈하려보니 Entities, Authorizer, Interactors가 하나의 거대한 컴포넌트가 되어버린다.
: 이 문제는 의존성 그래프에 순환이 있기 때문이다.
•
순환 끊기
: 컴포넌트 사이 순환을 끊고 의존성을 다시 비순환 방향 그래프로 원상복구하는 방법
1.
의존성 역전 원칙을 적용한다.
2.
Entities와 Authorizer가 모두 의존하는 새로운 컴포넌트를 만든다. 그리고 두 컴포넌트가 모두 의존하는 클래스를 새로운 컴포넌트로 이동시킨다.
⇒ 해당 해결책을 사용하면 요구사항이 변경되므로 컴포넌트 구조도 변경된다. 즉 컴포넌트 의존성 구조는 애플리케이션이 성장함에 흐트러지며 성장한다.
의존성 구조에 순환이 발생하는 지 항상 관찰해야 하며 순환이 발생하면 어떤식으로든 끊어야 한다.
하향식 설계
: 지금까지 논의로 결론을 지어보면 컴포넌트 구조는 하향식으로 설계될 수 없다는 것이다.
: 컴포넌트는 시스템에서 가장 먼저 설계될 수 있는 대상이 아니다. 시스템이 성장하고 변경되며 함께 진화하는 객체로
: 컴포넌트 의존성 다이어그램은 애플리케이션의 기능을 기술하는 일과 거의 관련이 없다.
: 컴포넌트 의존성 다이어그램은 애플리케이션의 빌드 가능성과 유지보수성을 보여주는 지도와 같다. 이러한 이유로 컴포넌트 구조는 프로젝트 초기에 설계할 수 없다.
SDP: 안정된 의존성 원칙
더 안정된 쪽에 의존하라
: 변경이 쉽지 않은 컴포넌트가 변동이 예상되는 컴포넌트에 의존하도록 만들어서는 절대로 안 된다. 한번 의존하게 되면 변동성이 큰 컴포넌트도 결국 변경이 어려워진다.
•
안정성
: 컴포넌트 안쪽으로 들어오는 의존성이 많아지면 상당히 안정적이라 볼 수 있다. 사소한 변경이라도 의존하는 모든 컴포넌트를 만족시키면서 변경하기는 어렵기 때문이다.
: 안쪽으로 들어오는 의존성이 많으면 안정적이며 적으면 불안정한 컴포넌트이다.
•
안정성 지표
: 어떻게 하면 안정성을 측정할 수 있을까? 여기서는 Fan-in과 Fan-out, I(불안정성)을 사용한다.
: I = Fan-out / (Fan-in + Fan-out)으로 나타내며 [0, 1]의 값을 가진다. I = 0이면 최고로 안정된 컴포넌트이며 I = 1이면 최고로 불안정한 컴포넌트란 뜻이다.
•
모든 컴포넌트가 안정적이어야 하는 것은 아니다.
: 위쪽에는 변경 가능한 컴포넌트가 아래쪽의 안정된 컴포넌트에 의존한다, 다이어그램에서 불안정한 컴포넌트를 관례적으로 위에 두는데, 위로 향하는 화살표가 존재한다면..
그건 큰일이기 때문이다.
•
추상 컴포넌트
: 오로지 인터페이스만을 포함하는 컴포넌트를 생성하는 방식인 추상 컴포넌트는 상당히 안정적이며 덜 안정적인 컴포넌트가 의존할 수 있는 이상적인 대상이다.
: 동적 타입 언어를 사용할 때는 이러한 추상 컴포넌트가 전혀 존재하지 않을 뿐더러 추상 컴포넌트로 향하는 의존성이 없다.
: 이들 언어에서의 의존성 구조는 훨씬 단순한데, 의존성을 역전시킬 때 인터페이스를 선언하거나 의존성을 상속받는 일이 전혀 필요하지 않기 때문이다.
SAP: 안정된 추상화 원칙
컴포넌트는 안정된 정도만큼 추상화되어야 한다.
•
고수준 정책을 어디에 위치시켜야 하는가?
: 시스템에는 자주 변경해서는 절대로 안되는 소프트웨어도 있다. 고수준 아키텍처나 정책 결정과 관련된 소프트웨어가 그 예다.
: 따라서 시스템에서 고수준 정책을 캡슐화하는 소프트웨어는 반드시 안정된 컴포넌트에 위치해야 한다.
: 만약 고수준 정책을 안정된 컴포넌트에 위치시키지 않는다면 그 정책을 포함하는 소스 코드는 수정이 어려워 진다.
: 어떻게 컴포넌트가 최고로 안정된 상태이면서도 동시에 변경에 충분히 대응할 수 있을 정도로 유연하게 만들 수 있을까?
•
안정된 추상화 원칙
: 안정된 추상화 원칙은 안정성과 추상화 정도 사이의 관계를 정의한다.
: 안정된 컴포넌트는 추상 컴포넌트어야 하며 안정성이 컴포넌트를 확장하는 일을 방해해서는 안 된다.
: 불안정한 컴포넌트는 반드시 구체 컴포넌트어야 하며 불안정하므로 컴포넌트 내부의 구체적인 코드를 쉽게 변경할 수 있어야 한다.
⇒ SAP와 SDP를 결합하면 컴포넌트에 대한 DIP나 마찬가지가 되버린다.
⇒ 핵심은 안정적인 컴포넌트라면 반드시 인터페이스와 추상 클래스로 구성되어 쉽게 확장할 수 있어야 한다.
추상화 정도 측정하기
NC: 컴포넌트의 클래스 개수
Na: 컴포넌트의 추상 클래스와 인터페이스의 개수
A: 추상화 정도. A = Na / Nc(A가 0이면 추상 클래스가 한개도 없고, 1이면 오로지 추상 클래스만 있음)
•
주계열
아래 그림은 안정성(I)과 추상화 정도(A) 사이의 관계 그래프다.
: 최고로 안정적이며 추상화된 컴포넌트는 (0, 1)에 위치한다.
: 최고로 불안정하며 구체화된 컴포넌트는 (1, 0)에 위치한다.
그림 14.12 A/I 그래프
: 모든 컴포넌트가 이 두 지점에 위치하는 것은 아니다.
: 아래 그림의 궤적은 컴포넌트가 절대로 위치해서는 안 되는 영역, 배제할 구역(Zone of Exclusion)이다.
그림 14.13 배제 구역(Zone of Exclusion)
•
고통의 구역
(0, 0) 주변 구역에 위치한 컴포넌트는 매우 안정적이며 구체적이다. 뻣뻣한 상태이다. 추상적이지 않아서 확장할 수 없고, 안정적이므로 변경하기 상당히 어렵다.(0, 0) 근처에 위치한 소프트웨어는 구체적인 유틸리티 라이브러리이다.하지만 변동성이 없는 컴포넌트는 (0, 0) 구역에 위치하더라고 해롭지 않다. 변동될 가능성이 없기 때문이다.
•
쓸모없는 구역
(1, 1) 주변의 컴포넌트는 최고로 추상적이지만, 누구도 그 컴포넌트에 의존하지 않는다.이러한 컴포넌트는 쓸모가 없다.따라서 이 구역은 쓸모없는 구역(Zone of Uselessness)이라고 부른다.
•
배제구역 벗어나기
변동성이 큰 컴포넌트 대부분은 두 배제 구역으로부터 가능한 한 멀리 떨어뜨려야 한다.최대한 멀리 떨어진 점의 궤적은 (1, 0)과 (0, 1)을 잇는 선분이다. → 저자는 주계열(Main Sequence)이라고 부름주계열 위 또는 가깝게 위치해야 하며, 이렇게 위치하면 '너무 추상적'이지도 않고, 추상화 정도에 비해 '너무 불안정'하지도 않다.
•
주계열과의 거리
지표를 통해 주 계열에서 멀리 벗어난 컴포넌트의 원인을 파악하고 조사하는 것이 중요하다.
결론
: 의존성 관리 지표는 설계의 의존성과 추상화 정도가 저자가 '훌륭한' 패턴이라고 생각하는 수준에 얼마나 잘 부합하는지를 측정한다.