SOLID 설계원칙

SOLID 설계원칙

Featured image

SOLID 원칙

SOLID 원칙은 함수와 데이터 구조를 클래스로 배치하는 방법으로, ‘클래스’란 단어를 사용했다고 해서 객체지향 소프트웨어에만 적용되는 것은 아니다

단순히 함수와 데이터를 결합한 집합의 의미로 클래스를 개념을 접근하여 SOLID 원칙에 접목한다.

SOLID 원칙의 목적

SOLID 원칙의 목적은 코드 수준보다 상위 개념인 모듈 수준에서 작업할 때 적용될 수 있다.

중간 수준의미란 하나의 큰 아키텍처 안에서 SOLID의 개념이 건축 개념으로 빗대어 볼 때 큰 건물을 짓는 재료 중의 하나인 벽돌 수준의 개념으로 이해해볼 수 있을 것 같다.

SRP(단일 책임 원칙)

각 SW 모듈은 변경의 이유가 단 하나여야만 한다.

보통 단 하나의 일만 해야 한다는 의미로 잘못 알고 있는데 이는 함수는 반드시 한 가지의 일만 해야 한다는 다른 원칙이 따로 있다. 이는 SOLID 원칙보다 더 저수준 개념에 해당하는 내용이다.

SRP란 다시말해, 오직 하나의 액터(actor)에 대해서만 책임져야한다. 하나의 모듈(소스파일)은 하나의 사용자, 또는 하나의 이해관계자에 대해서만 책임져야한다.

SRP 위반 사례

우발적 중복

SRP1

Employee 클래스 내에 세 가지 역할을 하는 메서드가 존재

세 가지의 액터(재무팀/인사팀/DBA)가 하나의 클래스에 결합된 상태.

이로 인해 일반적으로 하나의 클래스 내에 메소드들 중 중복되는 코드가 발생할 경우 private 메서드를 구현함으로써 중복 코드를 줄이려는 노력을 시도할 수 가 있는데,

만약 재무팀에서 calculatePay 기능을 수정하면서 중복 코드가 들어있는 private 메서드도 함께 수정하게 된다면 인사팀과 DBA에서 사용되는 기능들은 예상치 못한 코드 수정으로 에러를 야기할 수 있게 된다.

따라서, 서로 다른 액터가 의존하는 코드는 서로 분리하는 것이 좋다.

병합

하나의 Employee 클래스 내에 코드 중 재무팀과 인사팀에서 관리하고 있는 calculatePay 메서드와 reportHours 메서드를 동시에 수정하게 될 경우, 병합하는 과정에서 동일한 파일에 대한 수정을 진행했기 떄문에 충돌이 발생할 수 있다.

즉, 동일한 파일을 서로 다른 목적으로 소스 파일 수정을 시도하는 과정에서 코드 충돌이 발생할 수 있다.

해결책

  1. 클래스 분리

SRP2

BUT, 구현 시 일일히 세 클래스를 인스턴스화 하고 추적해야 하는 단점이 있다.

  1. Facade 패턴

SRP3

BUT, 다만 일부 개발자들은 중요한 핵심 기능은 Employee 클래스가 직접 들고 있어야 한다고 생각할 수 있기 때문에 위 방법을 선호하지 않을 수 있다.

  1. 덜 중요한 부분만 Facade 패턴 적용

SRP4

OCP(개방-폐쇄 원칙)

기존 코드를 수정하기보단, 반드시 새로운 코드를 추가하는 방식으로 시스템의 행위를 변경할 수 있도록 설계해야만 SW 시스템을 쉽게 변경할 수 있다.

OCP를 적용하기 위해서는 두 가지 원칙이 선행된 구조에서 제대로 그 목적을 발휘할 수 있다.

  1. SRP 적용
    • 서로 다른 목적으로 변경될 수 있는 요소를 적절하게 분리
  2. DIP 적용
    • 분리된 요소 사이의 의존성을 조직화함으로써 새로 조직화된 구조에서 하나의 요소가 그 행위가 확장했을 떄 다른 요소에는 영향이 발생하지 않음을 보장해야 한다.

즉, OCP 목적을 적용하려면 SRP와 DIP가 선행된 구조여야 한다.

OCP1

DIP

정보은닉

LSP(리스코프 치환 원칙)

상속 관계를 맺는 상호 관계는 서로 치환, 대체 가능한 관계여야 한다.

LSP의 올바른 예시

LSP1

LSP 위반 예시

LSP2

ISP(인터페이스 분리 원칙)

사용되지 않는 것에는 의존관계를 맺지 않아야 한다.

ISP의 잘못된 예시

ISP1

ISP의 적절한 예시

ISP2

ISP와 아키텍처

일반적으로 필요 이상으로 많은 걸 포함하는 모듈에 의존하는 것은 소스코드와 아키텍처 두 수준 모두에게 큰 비용을 치루게 한다. 불필요한 재컴파일과 재배포를 강제하기 때문이다.

DIP(의존성 역전 원칙)

고수준 정책을 구현하는 코드는 저수준 세부사항을 구현하는 코드에 의존해서는 안된다.

의존성 역전 원칙에서 말하는 ‘유연성이 극대화된 시스템’이란 소스 코드 의존성이 추상(abstraction)에 의존하며 구체(concretion)에는 의존하지 않는 시스템이다.

안정된 추상화

추상 인터페이스에 변경이 생기면 이를 구체화한 구현체들도 따라서 수정은 필연적이다.

반대로 구체적인 구현체에 변경이 생기더라도 그 구현체가 구현한 인터페이스는 변경될 필요가 없다.

인터페이스는 구현체보다 변동성이 낮다

즉, 안정된 SW 아키텍처란 변동성이 큰 구현체에 의존을 지양하고, 안정된 추상 인터페이스를 선호하는 아키텍처를 의미한다.

구체적인 코딩 실천법

팩토리

DIP

즉, 소스 코드의 의존성은 제어 흐름과는 반대 방향으로 역전되게 되는데 이러한 이유로 의존성 역전이라고 부른다.

DIP는 향후 아키텍처 다이어그램에서 가장 눈에 드러나는 원칙이 될 것이다.