SOLID

Jaca·2021년 12월 2일
0

여기저기 자주 등장하는 SOLID
제대로 정리하고 들어갈 필요가 있다고 느꼈다.

SOLID

SOLID란 로버트 마틴이 명명한 객체 지향 프로그래밍 및 설계의 다섯 가지 기본 원칙

프로그래머가 시간이 지나도 유지 보수와 확장이 쉬운 시스템을 만들고자 할 때 이 원칙들을 함께 적용할 수 있다.
SOLID 원칙들은 소프트웨어 작업에서 프로그래머가 소스 코드가 읽기 쉽고 확장하기 쉽게 될 때까지 소프트웨어 소스 코드를 리팩터링하여 안좋은 코드를 제거하기 위해 적용할 수 있는 지침이다.

  • SRP : 단일 책임 원칙 (Single Responsibility Principle)
    한 클래스는 하나의 책임만 가져야 한다.
  • OCP : 개방 폐쇄 원칙 (Open - Closed Principle)
    확장에는 열려 있으나 변경에는 닫혀 있어야 한다.
  • LSP : 리스코프 치환 원칙 (Liskov Substitution Principle)
    객체는 프로그램의 정확성을 깨뜨리지 않으면서 하위 인스턴스로 바꿀수 있어야 한다.
  • ISP : 인터페이스 분리 원칙 (Interface Segregation Principle)
    인터페이스 여러 개가 범용 인터페이스 하나보다 낫다.
  • DIP : 의존관계 역전 원칙 (Dependency Inversion Principle)
    추상화에 의존해야지 구체화에 의존하면 안된다.

SRP

작성된 클래스는 하나의 기능만 가지며 클래스가 제공하는 서비스는 하나의 책임을 수행하는데 집중되어 있어야 한다.
또 이 클래스의 변경은 오로지 하나의 이유만으로 변경이 이루어져야 한다.

SRP를 적용하면 책임 영역이 확실해지기 때문에 한 책임의 변경에서 다른책임의 변경으로의 연쇄작용을 없앨 수 있다.

확산적 변경

각각의 클래스는 하나의 책임, 변경에도 하나의 이유만 있어야 한다.
Extract Class(클래스 추출) 을 통해 책임을 각각의 클래스로 분할해야 한다.
중요한 점은 책임만 분리하는 것이 아니라 분리된 클래스 간의 복잡도를 줄여야 한다.

만약 Extract Class된 각각의 클래스들이 유사하고 비슷한 책임을 중복해서 갖고있다면 Superclass를 정의해 부모 클래스에 위임한다.

산탄총 수술

하나를 변경했을 때 많은 클래스를 고쳐야 하는 경우 산탄총 수술(Shotgun Surgery)가 필요하다.

변경을 할 때마다 많은 클래스를 조금씩 수정해야 한다면 Move Field와 Move Method를 기존의 클래스로 모으거나, 새 클래스를 만들어 해결한다.

즉 산발적으로 여러 곳에 분포된 책임을 한 곳에 모으는, 응집성을 높이는 작업이다.

OCP

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

요구사항의 변경이나 추가사항이 발생하더라도, 기존 구성요소의 수정이 일어나지 말아야 하며, 기존 구성요소를 쉽게 확장해서 재사용할 수 있어야 한다.

OCP는 관리가능하고 재사용한 코드를 만드는 기반이며, OCP의 가장 중요한 매커니즘은 추상화와 다형성이다.
OCP는 객체지향의 장점을 극대화하는 OOP의 기본 원리이다.

변경될 것과 변하지 않을 것을 구분하여 인터페이스를 정의하고,
정의한 인터페이스에 의존하도록 코드를 작성해야한다.

인터페이스 설계에서 적당한 추상화 레벨을 선택해야 한다.
추상화와 모듈을 분리하는 과정을 적절히 하지 못한다면 오히려 관계가 복잡해질 수 있다.

LSP

서브 타입은 언제나 기반 타입으로 교체할 수 있어야 한다.
즉, 서브 타입은 기반 타입이 약속한 규약을 지켜야 한다.

상속이든, 구현이든 궁극적으로 다형성을 통한 확장성을 목표로 한다.
다형성과 확장성을 극대화 하려면 하위 클래스보다는 상위 클래스를 사용하는 것이 더 좋다.
일반적으로 선언은 기반 클래스로 생성은 구체 클래스로 대입하는 방법을 사용한다.
생성 시점에서 구체 클래스를 노출시키기 꺼려질 경우 Abstract Factory 패턴 등을 사용하여 유연성을 높일 수 있다.

상속과 다형성은 따로 생각할 수 없다.
다형성으로 인한 확장 효과를 얻기 위해서 서브 클래스가 기반 클래스와 인터페이스를 어길 수 없으며, 이 구조는 OCP를 제공하게 된다.
결국 LSP는 OCP를 구성하는 구조이다.

상속을 통한 재사용은 기반 클래스와 서브 클래스 사이에 is-a 관계가 있을 경우로 제한되어야 하며, 그 외에는 조합(Composition)을 사용해야 한다.
하위 클래스가 상위 클래스 기능의 일부만 사용할 때(상속 거부), 하위 클래스가 상속 클래스와 IS-A 관계가 아니다.
상속보다는 조합

리스코프 치환원칙은 A 가 Object 를 상속받을 때 Object 타입의 변수로 A를 인스턴스화 했을 때에도 문제없이 사용가능해야한다는 것이다.

ISP

한 클래스는 자신이 사용하지 않는 인터페이스는 구현하지 말아야 한다.
즉 어떤 클래스가 다른 클래스에 종속될 때 가능한 최소한의 인터페이스만 사용해야 한다.

SRP가 클래스의 단일 책임을 강조한다면, ISP는 인터페이스의 단일 책임을 강조한다.
하지만 ISP는 어떤 클래스 혹은 인터페이스가 여러 책임과 역할을 갖는 것을 인정한다.
이런 경우에 ISP가 사용되는데 SRP가 클래스의 분리를 통해 변화에 적응성을 획득하는 반면, ISP에서는 인터페이스 분리를 통해 같은 목표에 도달한다.

클래스의 상속을 이용하여 인터페이스를 나눌 수 있다.
이와 같은 구조는 클라이언트에게 변화를 주지 않고, 인터페이스를 분리하는 효과를 갖는다.
하지만 상속을 이용한 확장은 상속받는 순간 인터페이스에 종속되어 제공하는 서비스의 성격이 제한된다.

위임을 이용하여 인터페이스를 나눌 수 있다. (조합과 유사)
만약 다른 클래스의 기능을 사용해야 하지만 그 기능을 변경하고 싶지 않다면, 상속 대신 위임을 사용한다.

DIP

의존관계의 역전(Dependency Inversion) 이란 구조적 디자인에서 발생하던 하위 레벨 모듈의 변경이 상위 레벨 모듈의 변경을 요구하는 위계 관계를 끊는 의미의 역전이다.
실제 사용 관계는 바뀌지 않으며, 추상을 매개로 메시지를 주고 받음으로써 관계를 최대한 느슨하게 만드는 원칙이다.

DIP는 의존 관계를 맺을 때 변화하기 쉬운 것 보다는 변화하기 어려운 것에 의존하라는 원칙이다.
변하기 어려운 추상적인 것을 표현하는 수단으로 추상 클래스와 인터페이스가 있다.

DIP를 만족하면 의존성 주입을 통해 변화를 쉽게 수용하는 코드를 작성할 수 있다.

profile
I am me

0개의 댓글