OOPDesign

소공 > SOLID  (웹 보기)
📌 OOP 설계 원칙 5가지

SRP (Single Responsibility Principle, 단일 책임 원칙)

📌 하나의 클래스는 하나의 Responsibility (책임) 만을 가져야 함
= High Cohesion (응집도)

  • ⭐ 각 책임별로 클래스 분리
  • 🔎 핵심 Business Entity인 Student 클래스의 정렬
    • 💔 Rigidity (경직성) ➡ 정렬이 필요 없는 클래스들까지 Change Propagation
      • Student 클래스는 어떤 순서로 정렬되어야 하는지 모름, Student를 정렬하고 싶은 건 Client, 그러므로 정렬은 Student 클래스의 본질적인 역할이 아님
      • Student 클래스에 이 책임을 지게 할 경우, 새로운 정렬 순서가 필요할 때마다 (핵심 Business Entity이므로) 많은 관련 클래스 및 Client들을 다시 재컴파일해야 함
    • 해결: 공통의 Interface Comparator를 상속받아, 각 Client에서 원하는 정렬 순서를 구현한 서비스 클래스들을 각각 구현하고 알맞은 Client에서 이를 참조하여 사용
  • 🔎 Rectangle 클래스를 여러 패키지에서 사용하는데, 각 패키지에서 활용하는 메소드가 다를 때
    • 💔 클래스 내부의 한 메소드의 변화가 그를 사용하지 않는 패키지에 Change Propagation
      • 한 패키지가 참조하는 메소드들만이 하나의 책임인 것, 즉 한 클래스에 두 개의 책임이 섞여 있는 것

    • 해결: 겹치는 부분만 공통의 abstract 클래스를 두고, 각 패키지에서는 abstract 클래스를 상속받아 각 패키지에 필요한 메소드만 추가 구현한 Concrete 클래스 참조

Responsibility (책임)

📌 Class의 제약 또는 의무
= Class가 수정되어야 하는 이유, 수정이 생길 수 있는 가능성

  • Class가 많이 변화할수록 Bug를 일으킬 가능성 높아짐, Change Propagation으로 인해 다른 곳에 영향을 끼치게 될 수 있음

God Object (신 객체)

📌 다른 객체와 협력하지 않고 혼자서 모든 것을 처리하는 객체, 너무 많은 책임이 있는 객체

Separation of Concerns (관심사의 분리)

🔎 인터페이스 계층과 비즈니스 로직 계층의 분리

OCP (Open Close Principle, 개방-폐쇄 원칙)

📌 확장에는 열려 있고 수정에는 닫혀 있어야 함, 특정 클래스의 행동을 수정 없이 확장할 수 있어야 함

  • 완전히 수정이 없을 수는 없으나, 구조적 변경과 같은 비싼 수정은 없어야 함
  • Abstraction (추상화) Level을 높여야
    • 상위의, 추상성 높은 부모 Interface/abstract 클래스는 Fixed한 필드만 넣어 정의
    • 상위의, 추상성 높은 클래스에 Dependency (의존성) 해야 함 ➡ 하위의 Concrete 클래스가 변하더라도, 부모 Interface/abstract 클래스만 그대로라면 Change Propagation 일어나지 않음
      ↔ 하위 Concrete Class에 의존, 변경사항 많이 생길 수 있으므로 부담됨, 의존된 쪽이 변하면 의존하는 쪽도 변해야 하므로
  • 일단은 변하지 않을 것이라 가정하고 OCP를 적용하지 않은 채로 간단하게 Design하여 구현한 뒤, 아래와 같은 상황을 Design 변경의 계기로 삼아 개선
    1. Change Propagation 의해 수정이 필요해지고,
      • 이러한 변경 계기를 최대한 빨리 잡아내는 것이 좋음 ➡ 🔎TDD, 테스트가 깨졌을 때
    2. 이러한 수정이 반복될 것이라는 근거가 있을 때,
      Rigidity (경직성)
  • 🔎 Employee를 상속받은 다양한 Concrete 직업 클래스들이 있고, Client에서는 각 Concrete 클래스 타입별로 조건문을 활용하여 동작

LSP (Liskov Substitution Principle, 리스코프 치환 원칙)

📌 Inheritance (상속)를 사용하기 위해서는, 자식 클래스는 그들의 부모 클래스를 의미/내용적으로 Substitution (대체) 할 수 있어야 함
= 부모 클래스 위치에서 자식 클래스가 Altering 없이 동작해야 함
= 부모와 자식 클래스가 IS-A Relationship 관계여야 함 (자식 ⊂ 부모)
!= 프로그램/컴파일 관점에서 부모 클래스 자리에 전달할 수 있어야 함 (이는 OOP 언어라면 당연한 것, 이것보다 더 좁은 의미)

DIP (Dependency Inversion Principle, 의존성 역전 원칙)

📌 High Abstraction (추상화) Level Module은 Low-Level에 Dependency (의존성)하면 안됨, 대신 Concrete를 추상화한 Interface/abstract 클래스에 의존해야 함
= Detail/Implementation에서 Interface/abstract 클래스에 의존해야 함, 반대 방향은 의존하면 안됨

Inversion (역전)

  1. Dependency (의존성)의 역전: Structured Analysis (구조적 분석설계)의 결과로 얻을 수 있는 Dependency를 반대로 뒤집어야 함

  2. Ownership (소유권)의 역전: 보통 Interface는 구현을 공급하는 Server쪽에서 소유하지만, Interface를 사용/호출하는 Client 쪽에서 Interface를 소유하고 Client 쪽에 맞게 이름을 짓도록 함
    • 🔎 Policy Layer에서 Policy Service를 소유하고 사용/호출 ↔ Mechanism Layer에서 Policy Service를 구현
    • ❤️ (생각) 사소한 변경의 경우, 변화가 아래 구현 Layer로 전파되지 않을 수 있음

ISP (Interface Segregation Principle, 인터페이스 분리 원칙)

📌 Client가 사용하지 않는 Interface에 의해 강제되어서는 안됨
= Client-Specific하게, 각 Client가 실제로 사용하는 것만 의존할 수 있도록 Interface를 Fine Grained (미세한 알갱이)로 나눠야
~= SRP (Single Responsibility Principle, 단일 책임 원칙)의 Interface 버전

  • 🔎 Student Enrollment, 한 클래스 안에 여러 Client를 위한 Interface가 포함됨

Fat Interface

📌 서로 다른 Client에 대한 Interface를 하나로 묶어 정의한 Interface
= Non-Cohesion (응집도) Interface