ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Design Pattern] SOLID Principle
    Learn/Architecture 2022. 8. 21. 17:04

    Design smell : 나쁜 디자인의 징후 --> 잘못된 의존성 때문에 나타남

     

    # SOLID Principle

    - The Single-Responsibility Principle (SRP)

    - The Open-Closed Principle (OCP)

    - The Liskov Substitution Principle (LSP)

    - The Interface Segregation Principle (ISP)

    - The Dependency Inversion Principle (DIP)

     

    # The Single-Responsibility Principle (SRP)

    클래스는 단 하나의 변화할 이유(책임)만 있어야 한다. 

    책임이란?

    - 해야하는 일

    - 많으면 많을수록 더 자주 변한다  > 버그가 생길 확률이 높아진다

     

    [예시]

    상황: student에 대한 sort가 필요해서 JAVA의 Comparable interface를 구현하고자 함

    Student class에 sort를 넣어주는건 SRP 위반이다. 

        - 이름, 학번 같은 것들에 비해 sort는 이질적이다. 

        - Student를 쓰는 많은 모듈들이 변화에 의해 영향을 받는다. 

     

    아래와 같이 Student에 compareTo를 넣으려 했으나 SRP 위반이다. 

    아래와 같이 해결할 수 있다. 

    Student는 그대로이고 SortStudentBySSN, SortStudentByName에 그 책임을 줬다. 

    Register같은 Student의 Source 또한 영향을 받지 않는다. 

    그러나 초반에 파악이 어려울 수 있다. 

    - 초반에는 responsibility가 어떻게 나뉠지 예상하는게 쉽지 않다. 

    - 그렇다고 무분별하게 SRP를 적용해서 Complexity를 높이는건 좋지 않다. 

     

    # The Open-Closed Principle (OCP)

    엔티티는 확장성 있어야 하지만 수정이 있을때 변경은 최소화 되어야 한다.  

     

    Abstraction이 중요하다. 

     

    [예시]

    HR매니저 모듈이 있을 때 아래와 같으면 새로운 타입이 들어올 때 마다 conditional statement를 추가해야 한다. 

    OCP를 만족시키려면 아래와 같이 작성하면 된다. 

    polymorphism을 이용해서 인스턴스에 각 기능을 위임한다. 

    SRP와 마찬가지로 미래를 예측할 수 없다. 

    과하게 적용하려고 하면 오히려 Complexity가 높아질 수 있다. 

        > TDD도 큰 도움이 될 수 있다. 

     

    # The Liskov Substitution Principle (LSP)

    subtype은 base type을 대체할 수 있어야 한다. 

        > 상속을 하는게 맞는지 아닌지 판단할 때 쓸 수 있음

     

    [예시]

    상황: Queue를 만들려고 하는데 이미 List에 만들어놓은 함수 Implementation을 활용하려고 상속받음

     

    그러나 Queue는 List보다 더 제한적임. (앞에서만 꺼내야됨)

        > Queue는 List를 대체할 수 없다. 

     

    Queue에서 List를 상속받는 대신 객체간의 결합(Composition)을 하면 기능만 API처럼 쓸 수 있다. 

     

    아래와 같은 경우 Implementation을 대체하려고 get함수를 deprecate했다. 

    문제는 사용자 입장에서는 하위 클래스의 인스턴스에서 get함수 사용시 에러가 발생한다. 

    (사용자는 부모것을 써야할지 자식것을 써야할지 알 수가 없다.)

     

    # The Dependency Inversion Principle (DIP)

    상위 모듈은 하위 모듈에 대한 의존성이 없어야 한다. 

    상위 모듈과 하위 모듈은 Abstraction에 대해 의존성이 있어야 한다. 

     

     

    일반적으로 아래와 같이 형태로 의존 관계를 만들기 쉬운데 좋지 않다

        - 개념적으로 가장 상위에 있는 Program이 Module과 Function에 의존성을 가지고 있다. 

            > Function은 자주 변하므로 좋지 않다. 

    아래와 같이 Interface를 통해 해결할 수 있다. 

    (Class를 모듈이라고 보면) Program과 Module이 직접적으로 의존성을 갖지 않는다. 

    DIP는 의존성 뿐만 아니라 오너쉽의 역전도 포함한다. 

        - 일반적으로는 서비스를 구현하는 쪽에서 오너쉽을 가져야 한다고 생각하기 쉽다. 

        - 하지만 오히려 클라이언트에서 오너쉽을 가져야 한다. 

     

    # The Interface Segregation Principle (ISP)

    꼭 가져야 하는 의존성만 갖는다. 

    필요 이상으로 뚱뚱한(fat) 인터페이스는 분리해준다. 

     

    [예시]

    상황: Student Enrollment에 있는 함수 중

        - getName, getSSN은 Roaster Application만 사용

        - getInvoice, postPayment는 Account Application만 사용

    문제는 Account App을 위해 getInvoice를 수정했는데 Roaster App도 영향을 받을 수 있다. 

    해결은 다음과 같이 인터페이스를 통해 API를 나눠준다. 

    각 App은 자신이 필요한 인터페이스만 가진다. 

     

    # QUIZ

    정답. 2번. LSP는 문법적 규칙이 아님. subtype이 base type을 대체할 수 없으면 위반이고 꼭 컴파일러가 에러를 보여주는건 아님. 

     

    1번 - X. 오히려 수행 속도는 느려질 수 있다. (indirection)

    2번 - O. 

    'Learn > Architecture' 카테고리의 다른 글

    [Design Pattern] Observer Pattern  (0) 2022.08.27
    [Design Pattern] Strategy Pattern  (0) 2022.08.26
    [Design Pattern] GRASP Principle  (0) 2022.08.24
    [Design Pattern] Object Oriented Paradigm  (0) 2022.08.20
    [Design Pattern] Introduction  (0) 2022.08.17

    댓글

Designed by Tistory.