본문 바로가기
클린아키텍처

[클린아키텍처] 7장~11장 설계원칙 - SOLID 원칙

by bzerome240 2022. 2. 27.

 

객체지향의 5가지 설계 원칙이 있다.

 

SRP
OCP
LSP
ISP
DIP


SRP (Single-responsibility principle) 단일 책임 원칙

: 하나의 모듈은 하나의 액터에 대해서만 책임져야 한다.

: 클래스는 단 하나의 기능만 갖고, 제공하는 서비스는 그 기능을 수행한다.

 

 

응집성

: 단일 액터를 책임지는 코드를 함께 묶어주는 힘

 

응집성을 위반하는 징후 1: 우발적 중복

응집성을 위반하는 징후 2: 병합

 

SRP 예제 참고

 

[SRP] 객체지향 설계 5 원칙 (1)

학습목표 : SRP[Single responsibility principle] 단일 책임 원칙 을 이해하고, 예제로 설명할 수 있다. SRP[Single responsibility principle] 단일 책임 원칙  - 한 클래스는 하나의 책임만 가져야 한다.  -..

old-developer.tistory.com

 

 

퍼사드(Facade) 패턴


OCP (Open Compute Project) 개방-폐쇄 원칙

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

기존 코드를 변경하지 않고 기능을 추가할 수 있어야 한다.

 

  • 동작 방식: Controller 또는 Interactor > Presenter > View
  • 기능이 how, why, when 발생하는지에 따라서 기능을 분리하고 분리한 기능을 컴포넌트의 계층구조로 조직화한다.
  • 목표: 저수준 컴포넌트에서 발생한 변경으로부터 고수준 컴포넌트를 보호할 수 있다.

 

처리 과정을 클래스 단위로 분할하고, 클래스는 컴포넌트 단위로 분리한다.

<I>: 인터페이스

<DS>: 데이터 구조

 

 

OCP 원칙 예제 

  • 카드 결제 시스템을 만들어야 한다.
  • 현재 지원하는 카드는 신한 카드 하나뿐이다.
  • 이제 우리 카드 결제가 추가되어 구현해야 한다.
  • 앞으로도 카드는 지속해서 추가될 예정이다

 


 

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

: 상위 타입의 객체를 하위 타입의 객체로 치환해도 코드가 문제없이 동작해야 한다는 원칙 (is-as)

 

 

LSP 위반 예제: 정사각형-직사각형 문제

  • 정사각형이 직사각형을 상속받는 구조에서, with height 값을 바꾸면 정사각형 변이 같아야 하므로 이는 LSP에 위반된다.
class Rectangle {
  private width: number;
  private height: number;

  constructor(width: number, height: number) {
    this.width = width;
    this.height = height;
  }

  public setWidth(width: number): void {
    this.width = width;
  }

  public setHeight(height: number): void {
    this.height = height;
  }

  public area(): number {
    return this.width * this.height;
  }
}

class Square extends Rectangle {
  constructor(width: number, height: number) {
    super(width, height);
  }

  public setWidth(width: number): void {
    this.width = width;
    this.height = width;
  }

  public setHeight(height: number): void {
    this.width = height;
    this.height = height;
  }

  public area(): number {
    return this.width * this.height;
  }
}

const jonyRectangle = new Rectangle(3, 5);
jonyRectangle.setWidth(5);
jonyRectangle.setHeight(3);
jonyRectangle.area() === 15; // true

const jewookRectangle = new Square(3, 3);
jewookRectangle.setWidth(5);
jewookRectangle.setHeight(3);
jewookRectangle.area() === 15; // false

 

LSP 지키려면 예제: 정사각형-직사각형 문제

  • 정사각형과 직사각형은 서로 다른 동작을 하는 클래스로 구현해야한다.
interface Shape {
  area(): number;
}

class Rectangle implements Shape {
  private width: number;
  private height: number;

  constructor(width: number, height: number) {
    this.width = width;
    this.height = height;
  }

  public setWidth(width: number): void {
    this.width = width;
  }

  public setHeight(height: number): void {
    this.height = height;
  }

  public area(): number {
    return this.width * this.height;
  }
}

class Square implements Shape {
  private side: number;

  constructor(side: number) {
    this.side = side;
  }

  public setSide(side: number): void {
    this.side = side;
  }

  public area(): number {
    return this.side * this.side;
  }
}

const jonyShape: Shape = new Rectangle(2, 8);
jonyShape.area() === 16; // true

const jewookShape: Shape = new Square(5);
jewookShape.area() === 25; // true

 

참고

 

[SOLID] 3. LSP(Liskov Substitution Principle) - 리스코프 치환 원칙

이번 글에서는 SOLID 원칙의 L에 해당하는 LSP에 대해 정리해보겠습니다. LSP(Liskov Substitution Principle) LSP는 리스코프 치환 원칙의 줄임말입니다. 리스코프 치환 원칙이란 상위 타입의 객체를 하위 타

wookgu.tistory.com

 


 

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

: 자신이 사용하지 않는 인터페이스에는 의존하면 안된다.

: 인터페이스를 작은 단위로 분리시킴으로써 한 클래스가 다른 클래스에 종솔될 때 가능한 최소한의 인터페이스만을 사용한다.

 

 

https://wedonttalknemore.tistory.com/17?category=967824

오퍼레이션을 인터페이스 단위로 분리하기 (왼쪽 분리 전, 오른쪽 분리 후)

 

 

정적 타입 언어 (Java) 는 import, use, include 선언문을 사용이 필요하다.

-> 소스 코드의 의존성 발생 -> 재컴파일, 재배포 필요!

 

동적 타입 언어 (Ruby, Python) 는 import, use, include 선언문이 존재하지 않는다.

-> 재컴파일, 재배포 필요 X

 

ISP는 언어 종류에 따라 영향받는 정도가 다르다.

 

잘못된 아키텍처 예시

https://wedonttalknemore.tistory.com/17?category=967824

S라는 시스템 구축 시 F 프레임워크 도립을 원하며, D 데이터베이스를 반드시 사용하도록 만들경우

-> S는 F에 의존, F는 D에 의존

-> 의존하게 되면 문제가 많이 발생할 수 있다.

 


 

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

: 소스 코드 의존성이 추상에 의존하며 구체에는 의존하지 않는 시스템

ex) 자바 같은 정적 타입 언어에서 use, import, include 구분은 오직 인터페이스나 추상 클래스 같은 추상적인 선언만을 참조해야한다. (구체적인 대상에는 의존하면 안된다)

: 상위 계층이 하위 계층의 구현으로부터 독립되게 하는 원칙

  • 소스코드 의존성은 제어흐름과는 반대 방향으로 역전된다.
  • 유연성이 극대화된 시스템
  • 아키텍저 다이어그램에서 가장 눈에 띄는 원칙

 

변하지 않는 것 vs 변하기 쉬운 것

  • 인터페이스, 추상 클래스 -> 변하지 않는 것
  • 구체 클래스 -> 변하기 쉬운 것   ex) 우리가 개발중이라 자주 변경될 수 밖에 없는 모듈들

 

 

소프트웨어 설계의 기본

  • 인터페이스의 변동성을 낮추기 위해 노력한다.
  • 인터페이스를 변경하지 않고도 구현체에 기능을 추가할 수 있는 방법을 찾기 위해 노력한다.

 

DIP 코딩 실천법

  • 변동성이 큰 구체 클래스를 참조하지 않기 대신에 추상 인터페이스를 참조하기
  • 변동성이 큰 구체 클래스로부터 파생하지 않기
  • 구체 함수를 오버라이드 하지 않기
  • 구체적이며 변동성이 크다면 절대로 그 이름을 언급하지 않기

 

의존성을 관리하기 위해 추상 팩토리 패턴을 사용한다.

 

https://velog.io/@no-brand/%EB%94%94%EC%9E%90%EC%9D%B8%ED%8C%A8%ED%84%B4-SOLID-DIP-Dependency-Inversion-Principal-%EC%9D%98%EC%A1%B4%EC%84%B1-%EC%97%AD%EC%A0%84-%EC%9B%90%EC%B9%99

DIP 위배를 모두 없앨 수는 없다.

But 위배하는 클래스들을 구체 컴포넌트 내부로 모으면, 시스템의 나머지 부분과 분리할 수 있다.

대다수의 시스템은 이러한 구체 컴포넌트 (ex. Main) 를 최소한 하나는 포함한다.

 

 

참고

 

[디자인패턴] SOLID | DIP (Dependency Inversion Principal), 의존성 역전 원칙

목적 : 유연셩이 극대화된 시스템설명 : 의존성이 구현 (implementation) 에 의존하지 않고, 추상 (abstraction, interface) 에 의존하는 형태DIP 의 아이디어 자체만 보면 비현실적 입니다. 대부분의 언어가 c

velog.io

 

 

 


 

 

클래스간의 관계 종류 화살표

https://acet.pe.kr/150
자바로 배우는 리팩토링 입문 (유키 히로시)

 

 

 

728x90
반응형

댓글