큰 애플리케이션을 조직화할 때는 클래스보다 더 큰 단위가 필요하다. 이때 패키지를 사용하여 그 단위를 잡는다. 이번 챕터에서는 이 패키지를 설계하는 6가지 원칙에 대해 설명한다. 원칙은 총 6개로 3개 / 3개 씩 그 종류가 아래와 같이 구분된다.
- 3개 → 패키지 응집도에 대한 원칙, 클래스를 패키지에 할당하는 일을 도움
- 3개 → 패키지 결합도에 대한 원칙, 패키지 간의 관계를 결정하는 일을 도움
- 추가적으로 설계 안에 존재하는 의존성 구조를 측정하고 특징을 찾을 수 있게 해주는 의존성 관리 측정법 소개 [책 내에서 소개]
- REP (재사용 릴리즈 등가 원칙)
- 재사용의 단위가 릴리즈의 단위이다.
- 재사용 단위는 릴리즈 단위보다 작을 수 없다. (여기서 재사용 단위의 예는 패키지가 될 수 있다.)
- 재사용성은 반드시 패키지에 기반을 두어야 한다. → 재사용 가능한 패키지는 재사용 가능한 클래스를 포함해야한다.
- SW가 재사용될 예정이라면, 그 목적을 가진 사람이 편리하게 느낄 방식으로 분할
- 잠재적인 재사용자의 입장에서 봐야한다.
- 패키지에 재사용될 소프트웨어가 들어 있다면 그 패키지에는 재사용을 목적으로 설계되지 않은 소프트웨어는 들어 있지 않아야 한다. ⇒ 즉 패키지의 모든 클래스가 재사용 가능하든, 아니면 모두 그렇지 않아야한다!
- 패키지 안의 모든 클래스는 동일한 재사용자를 고려한다.
- 재사용성이 유일한 기준이 아닌 누가 재사용자인지도 고려한다.
- CRP (공통 재사용 원칙)
- 패키지 안의 클래스들은 함께 재사용되어야 한다. 어떤 패키지의 클래스 하나를 재사용한다면 나머지도 모두 재사용한다. (위에서 언급한 이야기와 유사)
- 어떤 클래스들이 패키지에 포함되어야 하는지 결정할 때 도움이 된다.
- 자주 함께 재사용되는 클래스들은 동일한 패키지에 위치시킨다.
- CRP를 따른 패키지 안을 보면 클래스들이 서로 상당한 정도의 의존 관계를 맺고 있다.
- CRP는 어떤 클래스를 같은 패키지에 넣지 않아야 할지도 말해준다.
- 어떤 패키지에 의존한다면 그 패키지의 모든 클래스에 의존하는지 확실히 해두어야한다.
- CRP에 따르면 클래스 관계로 서로 단단히 묶여 있지 않은 클래스들은 같은 패키지에 넣지 말아야한다.
- CCP (공통 폐쇄 원칙)
- 같은 패키지 안의 클래스들은 동일한 종류의 변화에는 모두 폐쇄적이어야 한다. 패키지에 어떤 변화가 영향을 미친다면, 그 변화는 그 패키지의 모든 클래스에 영향을 미쳐야 하고 다른 패키지에는 영향을 미치지 않아야한다.
- 대상이 패키지인 SRP이다. → 패키지를 변경할 이유가 여러가지면 안된다는 것이다.
- 코드를 변경해야한다면 우리는 여러 패키지를 고치는 것보다 해당하는 패키지 하나만 고치고 싶을 것이다. → 이것이 최종 목표이다.
- 동일한 이유로 변할 것 같은 클래스들은 한 장소에 모아놓으라고 권장
- 클래스 A, B가 물리적으로나 개념적으로나 단단히 결합되어 있어 항상 함께 변경된다면 → A, B는 같은 패키지이다.
- 특정 종류의 변화에 개방되어 있는 클래스들은 같은 패키지로! → 요구사항 변경시 그에 따라 변경할 패키지수를 최소화!
- ADP (의존 관계 비순환 원칙)
- 패키지 의존성 그래프에서 순환을 허용하지 말라.
- 패키지의 의존 관계 구조에 순환이 있으면 안된다.
- 패키지 다이어그램은 비순환 방향 그래프가 되어야한다.
- 왜 순환이 없어야 하지? → 변화로 인해 생기는 영향이 상대적으로 작아진다.
- 패키지에 순환이 발생하면 그 패키지들은 언제나 서로의 일에 간섭할 수 밖에 없게되고 그로 인해 생기는 영향이 매우 커진다.
- 책의 예시를 보면 결국 의존을 따라가다 보면 모든 패키지에 의존하게 되고 → 그만큼 의도치 않은 변경이 생기게 될 가능성이 높아지게 된다.
- 이미 순환이 존재하면? -> 이러한 순환을 끊는 방법은 크게 2가지가 존재
- DIP 적용 → 이때 클라이언트를 중심으로 인터페이스 이름을 지어야한다는 것에 주의
- 순환이 일어났던 원인 패키지 A, B가 둘 다 의존하는 새로운 패키지 생성
- 패키지 구조는 하향식으로 설계하기 매우 힘들다. → 패키지 구조는 상향식으로 설계해야한다.
- 패키지구조는 시스템이 성장하고 변화하며 같이 진화
- 패키지 의존 관계 다이어그램은 애플리케이션의 기능을 기술하는 일과는 거의 아무런 관계가 없다는 것에 주의!
- 결국 우리는 변화의 영향을 줄이기 위해 SRP와 CCP에 주의를 기울여서 함께 변경되기 쉬운 클래스들을 함께 묶어놓는 것이 목적
- 애플리케이션 성장에 따라 재사용 가능한 요소를 만드는 일도 고려 → CRP 적용
- 이때 순환이 발생하면 ADP 적용 → 패키지 구조가 흔들리면서 성장
- 공통으로 무엇을 폐쇄해야할지, 재사용 가능한 요소가 어떤 것인지도 모르고 클래스를 설계하기 전에 패키지 의존 관계 구조를 먼저 설계하려하면 큰 실패를 맛볼 수 있다.
- SDP (안정된 의존 관계 원칙)
- 의존은 안정적인 쪽으로 향해야 한다.
- 안정적이다! → 그만큼 변경을 하려면 매우 많은 힘을 써야한다!
- 모든 패키지가 안정적일 필요는 없다!
- 단 패키지의 의존 방향은 “불안정 → 안정” 으로 흘러야한다! I(불안정성)이 작아지는 방향으로 흘러야한다!
- 그 반대가 되면, 변경이 쉬운 패키지도 변경이 어려워지게 된다!
- 안전성이 높다! → 변경하기 힘들다 → 다른 많은 패키지가 그 패키지에 의존하고 있다!
- 패키지 X가 다른 패키지에 의존하지 않는다! → 외부의 영향이 없다! → 독립적
- X패키지에 의존하는 패키지가 3개이다 → 3개의 패키지에 책임이 있다!
- 기타 책에서 안정성 측정법에 대한 설명이 있다.→ 수치화시키는 방법
- SDP가 위반되는 사례에서도 DIP를 이용하여 상황을 바로잡을 수 있다.
- SAP (안정된 추상화 원칙)
- 패키지는 자신이 안정적인 만큼 추상적이기도 해야 한다.
- 안정적인 패키지는 그 안정성 때문에 확장이 불가능하지 않도록 추상적이어야한다! ( 유연함을 위해)
- 불안정한 패키지는 구체적이어야하며 그 불안정성이 패키지 안의 구체적인 코드가 쉽게 변경될 수 있도록 허용한다. => 굳이 추상화를 할 필요가 없다는 뜻인 듯하다! 어차피 자주 바뀔 것이고 일반적으로 안정적인(다른 것들이 많이 의존하는) 쪽이 더 중요하고 메인 도메인 로직인 경우가 많기에, 그 쪽에 더 확장을 위한 힘을 쓰라는 것인 듯 하다. 굳이 바뀔 가능성이 많고 이쪽을 의존하는 패키지가 거의 없는 불안정한 패키지까지 추상화를 진행하며 코드 수, 클래스 수, 복잡도를 증가시킬 필요없다는 뜻이라고 해석했다.
- 확장이 가능한 안정적인 패키지는 유연하며, 설계를 지나치게 제약하지 않는다.
- SAP + SDP → DIP의 패키지판 ⇒ SDP는 의존 관계의 방향이 안정성의 증가 방향과 같아야 한다, SAP는 안정성이란 추상성을 내포 ⇒ 의존 관계는 추상성의 방향으로 흘러야한다!
⇒ 패키지 구성은 프로젝트의 초점이 개발 용이성에서 재사용성으로 옮겨감에 따라 시간이 흐르면서 조금씩 흔들리며 진화한다.
"안정성"이라는 개념에 대해 더 확실하게 이해한 것 같아서 개인적으로 얻어간 것이 많은 챕터였다. 또한 SOLID 원칙에 대해서만 공부하고 패키지 단위의 원칙 6개에 대해서는 전혀 알지 못했는데, 이번 기회에 이렇게 공부하게 되어서 좋았다. 물론 아직 어색한 원칙들이기에 천천히 이해해나가야 할 것 같다.