ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • SOLID 원칙
    Software Architecture 2024. 3. 19. 06:37
    728x90

     

    좋은 설계란, 시스템에 새로운 요구사항이나 변경사항이 있을 때 영향을 받는 범위가 적은 구조

    —> 시스템에 예상하지 못한 변경사항이 발생하더라도, 유연하게 대처하고 추후 확장성이 있는 시스템 구조를 만들 수 있다.

     

    • 객체지향 개발 5대 원리

     

    SRP, Single Responsibility Principle

    : 작성된 클래스는 하나의 기능만 가지며 클래스가 제공하는 모든 서비스는 그 하나의 책임을 수행하는 데 집중되어 있어야 한다는 원칙

     

    • 어떤 변화에 의해 클래스를 변경해야 하는 이유는 오직 하나여야 한다
    • 책임 영역이 확실해지기 때문에 한 책임의 변경에서 다른 책임의 변경으로의 연쇄작용에서 자유로울 수 있음
    • 책임을 적절히 분배함으로써 코드의 가독성 향상, 유지보수 용이

     

    적용방법

    1. 여러 원인에 의한 변경
    • Extract Class를 통해 혼재된 각 책임을 각각의 개별 클래스로 분할하여 클래스 당 하나의 책임만을 맡도록 하는 것
    • 책임만 분리하는 것이 아닌, 분리된 두 클래스 간의 관계 복잡도를 줄이도록 설계하는 것
    • Extract Class된 각각의 클래스들이 유사하고 비슷한 책임을 중복해서 갖고 있다면 Extract Superclass를 사용할 수 있다.

     

    영수증 텍스트 추출을 할 시에 서식에 따라 분류를 해야하는 데, 각 영수증들의 공통점은 외래/입원/중간/퇴원 코드는 맨 위에 있었던 점 —> 이것들은 부모 가상 클래스를 생성하여 관련 구분 코드를 위임하여 적용했다

     

    1. 산탄총 수술
    • Move Field와 Move Method를 통해 책임을 기존의 어떤 클래스로 모으거나, 이럴만한 클래스가 없다면 새로운 클래스를 만들어 해결할 수 있다.
    • 즉 산발적으로 여러 곳에 분포된 책임들을 한 곳에 모으면서 설계를 깨끗하게 할 수 있다.

     

    고유정보/변화요소로 분리

    —> 고유 정보 : 동종의 다른 클래스(객체)와 구분되는 정보 / 변화 요소 : 특성 정보군으로, 변경이 발생할 수 있는 부분

     

    SRP의 적용 대상은 변화 요소이다.

     

    *주요원칙

    • 클래스는 자신의 이름이 나타내는 일을 해야 한다.
    • 각 클래스는 하나의 개념을 나타내어야 한다
    • 사용되지 않는 속성이 결정적 증거
    • 무조건 책임을 분리한다고 해서 SRP가 적용된 것이 아니며, 각 개체 간의 응집력이 있다면 병합이 순 작용의 수단이 될 수 있다.

     

    OCP, Open Close Principle

    : 소프트웨어의 구성요소(컴포넌트, 클래스, 모듈, 함수)는 확장에 열려있고, 변경에는 닫혀있어야 한다.

    • 변경을 위한 비용은 가능한 줄이고 확장을 위한 비용은 가능한 극대화 해야 한다.
    • 요구사항의 변경이나 추가사항이 발생하더라도, 기존 구성요소는 수정이 일어나지 않아야 하며, 기존 구성요소를 쉽게 확장해서 재사용할 수 있어야 한다.
    • 클래스를 확장을 통해 손쉽게 구현하면서, 확장에 따른 기존 클래스 수정은 최소화해야 한다.

     

    OCP는 관리 가능하고 재사용 가능한 코드를 만드는 기반이며, OCP를 가능하게 하는 중요 메커니즘은 추상화와 다형성.

     

     

    적용방법

    1. 변경될 것과 변하지 않을 것을 엄격히 구분
    2. 이 두 모듈이 만나는 지점에 인터페이스를 정의
    3. 구현에 의존하기보다 정의한 인터페이스에 의존하도록 코드 작성

     

    코드의 수정을 최소화하여 결합도는 줄이고 응집도는 높이는 효과.

     

    *적용이슈

    • 확장되는 것과 변경되지 않는 모듈을 분리하는 과정에서 크기 조절에 실패하면 오히려 관계가 더 복잡해진다.
    • (Spring) 인터페이스는 가능하면 변경되어서는 안된다

     

    LSP, The LisKov Substitution Principle

    : 서브 타입은 언제나 기반 타입으로 교체할 수 있어야 한다.

     

    • 서브 타입은 언제나 기반 타입과 호환될 수 있어야 한다
    • 서브 타입은 기반 타입이 약속한 규약을 지켜야 한다
    • 상속은 구현 상속이든 인터페이스 상속이든 궁극적으로 다형성을 통한 확장성 획득을 목표로 한다
    • 서브 클래스가 확장에 대한 인터페이스를 준수해야 함을 의미한다.
    • 다형성과 확장성을 극대화하려면 하위 클래스를 사용하는 것 보다는 상위 클래스를 사용하는 것이 좋다.
    • 선언은 기반 클래스로, 생성은 구체 클래스로 대입하는 방법을 사용
    • 상속을 통한 재사용은 기반 클래스와 서브 클래스 사이에 IS-A 관계가 있을 경우로만 제한되어야 한다.
    • 다형성으로 인한 확장 효과를 얻기 위해서는 서브 클래스가 기반 크랠스와 클라이언트 간의 규약을 어겨서는 안된다.
    • 객체지향 설계 원리는 서로가 서로를 이용하기도 하고 포함하기도 하는 특징이 있다.
    • LSP : 규약을 준수하는 상속구조를 제공
    • OCP는 확장하는 부분에 다형성을 제공해 변화에 열려있는 프로그램을 만들 수 있도록 한다
    • 부모 메서드의 오버라이딩을 조심스럽게 따져가며 진행해야 한다

     

    적용방법

    1. 만약 두 개체가 똑같은 일을 한다면 둘을 하나의 클래스로 표현하고 이들을 구분할 수 있는 필드를 둔다
    2. 똑같은 연산을 제공하지만, 이들을 약간씩 다르게 한다면 공통의 인터페이스를 만들고 둘이 이를 구현한다.
    3. 공통된 연산이 없다면 완전 별개인 2개의 클래스를 만든다
    4. 두 개체가 하는 일에 추가적으로 무언가를 더 한다면 구현 상속을 사용

     

     

    *적용이슈

    • 혼동될 여지가 없고 트레이드 오프를 고려해 선택한 것이라면 그대로 둔다
    • 다형성을 위한 상속 관계가 필요없다면 Replace with Delegation을 한다.
    • 상속은 깨지기 쉬운 기반 클래스를 지니고 있으므로 Is-A 관계가 성립되지 않는다
    • 상속 구조가 필요하다면 Extract Subclass, Push Down Field, Push Down Method 등의 리팩토링 기법을 이용하여 LSP를 준수하는 상속 계층 구조를 구성
    • IS-A 관계가 성립한다고 프로그램에서까지 적용되는 것은 아니며, 이들의 역할, 이들 사이에 공유하는 연산이 있는지, 그리고 이들 연산이 어떻게 다른지 등을 종합적으로 검토
    • Design BY Contract 적용

     

    ISP, Interface Segregation Principle

    : 자신이 사용하지 않는 인터페이스는 구현하지 말아야 한다. 

     

    • 어떤 클래스가 다른 클래스에 종속될 때는 가능한 최소한의 인터페이스만을 사용해야 한다.
    • ISP = ‘하나의 일반적인 인터페이스보다는, 여러 개의 구체적인 인터페이스가 낫다’
    • 어떤 클래스를 이용하는 클라이언트가 여러 개고 이들이 해당 클래스의 특정 부분집합만을 이용한다면, 이들을 따로 인터페이스로 빼내어 클라이언트가 기대하는 메시지만을 전달할 수 있도록 한다.
    • 인터페이스의 단일 책임을 강조.
    • 한번 인터페이스를 분리하여 구성해놓고 나중에 무언가 수정사항이 생겨서 또 인터페이스를 분리하는 행위를 가하지 않아야 한다

     

    적용방법

    1. 클래스 인터페이스를 통한 분리
    • 클래스의 상속을 이용하여 인터페이스를 나눌 수 있다.
    1. 객체 인터페이스를 통한 분리
    • 위임을 이용하여 인터페이스 분할

     

     

    JAVA Swing의 JTable

    : 모든 서비스를 필요로 하는 객체에게 기능 전부를 노출하지만, 이벤트 처리와 관련해서는 여러 리스너 인터페이스를 통해 해당 기능만 노출

     

    *적용이슈

    • 기 구현된 클라이언트에 변경을 주지 말아야 한다
    • 두 개 이상의 인터페이스가 공유하는 부분의 대상을 극대화
    • 서로 다른 성격의 인터페이스를 명백히 분리

     

    DIP, Dependency Inversion Principle

    : 구조적 디자인에서 발생하던 하위 레벨 모듈의 변경이 상위 레벨 모듈의 변경을 요구하는 위계관계를 끊는 의미의 역전

     

    • 실제 사용 관계는 바뀌지 않으며, 추상을 매개로 메시지를 주고 받음으로써 관계를 최대한 느슨하게 만드는 원칙

     

    • IoC / Hook Method / 확장성의 3가지 요소가 조합되어 복잡한 컴포넌트들의 관계를 단순화하고 컴포넌트 간의 커뮤니케이션을 효율적이게 한다.

     

    • 상위 클래스에서 처리의 흐름을 제어하며, 하위 클래스에서 처리의 내용을 구체화하는 것
    • 코드의 중복을 줄이고, 리팩토링에 유리한 패턴으로 상속을 통한 확장 개발 방법으로써 많이 사용되는 패턴 중에 하나.
    • 구현 클래스에 의존하지 말고, 인터페이스에 의존

     

    적용방법

    Layering

    : 잘 구조화된 객체지향 아키텍처들은 각 레이어마다 잘 정의되고 통제되는 인터페이스를 통한 긴밀한 서비스들의 집합을 제공하는 레이어들로 구성

    —> Transitive Dependency가 발생했을 때 상위 레벨의 레이어가 하위 레벨의 레이어를 바로 의존하게 하는 것이 아니라 이 둘 사이에 존재하는 추상레벨을 통해 의존해야 하는 것.

     

     

    소프트웨어가 고객이 원하는 기능을 하도록.

    객체지향 기본원리를 적용해서 소프트웨어를 유연하게.

    유지보수와 재사용이 쉬운 디자인을 위해 노력.

    728x90
Designed by Tistory.