이 글은 principles of package design 원서를 읽고 정리해둔 글입니다.
오역이 잘못된 해석이 존재 할 수 있습니다.
- 클래스 디자인의 기본 원칙: 역자는 패키지 디자인을 다루기 위해 클래스 디자인의 기본 원칙을 일부 차용하기 때문에 SOLID 원칙을 가볍게 설명한다
- SRP (단일 책임 원칙)
- 객체는 하나의 책임을 가지고 하나의 변경 이유를 가져야 한다
- 단일 책임 원칙이 지켜졌을 때 장점
- 테스트 하기가 용이하다
- 같은 변경에 대해 여러 코드가 변경되지 않는다
- 단일 책임 원칙이 깨졌음을 판별하는 방법
- 너무 많은 인스턴스를 소유할 때
- 너무 많은 Public 메서드를 가질 때
- 클래스의 메서드가 너무 다양한 인스턴스를 사용 할 때
- 특별한 작업이 private 메서드로 존재 할 때
- OPEN-CLOSE
- 코드의 변경 없이 코드의 행위를 확장할 수 있어야 한다
- 즉, 상위 클래스의 변경 없이 하위 클래스의 교체 만으로 행위 변경이 가능해야 한다
- 상속에 관한 원칙이 아니다!!
- 장점
- 위 원칙이 지켜지는 코드는 다양한 상황에서 사용이 가능해 진다
- 자연스럽게 DI 원칙이 지켜지게 된다
- 인터페이스를 제공하게 됨으로 사용자가 행위를 유연하게 변경이 가능하게 된다
- OCP 원칙이 깨졌음을 판별하는 방법
- 행위를 선택하기 위한 컨디션(if 문)이 들어 있다
- 반복되는 컨디션 문장이 들어간다
- 하드코딩된 다른 클래스 참조나 클래스 이름이 들어간다
- 클래스 안에서 새로운 객체를 생성한다 → 새로운 규칙이 생성됨을 의미
- 클래스가 protected 속성을 가진 프로퍼티나 메서드를 가진다
- Liskov Substitution(리스코프 치환 원칙)
- 상속된 객체는 상위 객체와 치환 가능해야 한다
- LSP 원칙이 깨졌음을 판별하는 방법
- 상속된 객체가 상위 객체의 모든 구현을 가지지 않는다
- 부족한(상황에 맞지 않는) 추상화
- 상속된 여러 객체의 같은 메서드가 서로 다른 type을 반환할 때
- 상속된 객체가 원래 객체보다 더 적은 기능을 제공할 때 → 특정 아규먼트에 대해서 에러를 발생시키는 등등..
- 부모 객체보다 더 많은 함수를 가지고 있을 때
- interface separation
- **인터페이스(클래스는 아님!!!)**는 사용자의 요구 사항에 맞게 잘 정의되어야 한다
- ISP 원칙이 깨졌음을 판별하는 방법
- 다양한 유즈케이스가 발생한다
- 인터페이스가 아닌 직접 클래스를 사용할 때
- 사용자가 불필요한 메서드를 볼 수 있을 때
- 인터페이스를 만들어야 하는 클래스
- 모든 퍼블릭 메서드가 클라이언트에게 사용되지 않을 때
- 클래스가 I/O 작업과 같은 시스템 자원을 사용 할 때
- 클래스가 서드파티 코드를 직접적으로 사용 할 때
- 다양한 여러 구현이 있음을 알리고 싶을 때
- 객체 그래프가 변경될 것임이 예측 가능 할 때
- 클래스로만 써야 하는 클래스
- 엔티티와 Value Object
- 특정 비즈니스 로직에 관한 연산만 가지는 경우
- 상태만 가지는 객체(ex: DTO )
- 서비스 객체
- Dependency inversion
- 추상화에 의존하고 구체화를 멀리하라
- 항상 추상화에 의존하도록 코딩하라
- DSP 원칙이 깨졌음을 판별하는 방법
- 상위 클래스가 하위 클래스에 의존한다
- 벤더 락인 → 서드파티 코드에 직접적으로 의존하는 것을 말한다
-
패키지 디자인의 판단 기준
- 응집성: 연관된 것들의 차수( 집합의 정도 )
- 응결성: 다른 것들과 연관되는 차수 ( 연관성의 정도 )
-
패키지 응집성을 다루는 3원칙
- Common Reuse Principle
- 함께 사용되는 클래스는 같은 패키지에 존재해야 한다
- 함께 사용될 수 없는 클래스는 같은 패키지에 존재해서는 안된다
- 패키지끼리는 링커처럼 동작해서 연결되어야 한다
- 옵션(ADD-ON) 패키지는 모듈에서 분리하는 것이 좋다
- Common Closure Principle
- 같은 변경에 대해 영향을 받는 모든 클래스는 같은 패키지에 존재해야 한다
- 모듈은 주로 수직(도메인)으로 분할되며, 각각의 모듈은 레이어 형태로 수평 분할되는 경우가 많다
- Release/Reuse Equivalence Principle
- 릴리즈의 범위는 재 사용성의 범위와 같아야 한다
- SCM(like Git)의 설명은 문서처럼 다뤄야 한다
- Deprecated된 메서드는 신규 메서드와 연결되어야 하며 다음 major 릴리즈에 제거되어야 한다
- 버전 연관성은 낙관적으로 운용해야 한다
- Primitive 대신 값 객체를 이용하라
- 응집성있는 패키지 설계 가이드
- 처음에는 CRP의 원칙을 따라 설계한다
- 유지보수의 용이성을 위해 CCP의 원칙에 따라 분해한다
- 마지막으로 REP의 원칙을 지키지 위해 이동한다
-
패키지 응결성을 다루는 3원칙
- Acyclic Dependencies Principles
- 패키지 간의 사이클이 존재해서는 안된다는 원칙
- 클래스 간의 사이클 또는 패키지 사이클은 인터페이스를 이용하여 분리가 가능하다
- 도메인 이벤트 디스패치 방식을 이용하여 제거 가능하다
- 사이클의 방향성을 생각해야 한다 ( 서로 연결되어 있다고 전부 사이클은 아니다 )
- Stable Dependencies Principle
- Stable Abstraction Principle
- 서비스 클래스는 변경에 완전히 닫혀있도록 작성하는 것을 역자의 전 책에서는 추천하고 있다
- 실제 구현과 인터페이스의 패키지는 분리되어야 한다고 역자는 생각한다
- 재 사용 가능한 컴포넌트를 생성하기 위한 조건