Posts [Design Pattern] SOLID 원칙 (SOLID Principles)
Post
Cancel

[Design Pattern] SOLID 원칙 (SOLID Principles)

SOLID Principles?

  • 로버트 C. 마틴이 명명한 객체지향 프로그래밍 및 설계의 기본 원칙이다.
  • 프로그래머가 유지보수가 쉽고, 확장성이 있는 프로그램을 개발하려 할 때 이 원칙들을 적용할 수 있다.

SRP (Single Responsibility Principle)

  • 단일 책임 원칙
  • 한 객체는 단 하나의 책임만 가져야 한다.
  • 객체지향적으로 설계할 때는 응집도는 높게, 결합도는 낮게 설계해야 한다.

응집도는 모듈 내부의 기능적인 응집 정도이고, 결합도는 모듈과 모듈간의 상호 결합 정도이다.

  • 객체에 책임이 많아질수록 객체 내부에서 서로 다른 역할을 수행하는 코드끼리 강하게 결합할 가능성이 높아진다.
  • 객체마다 책임을 제대로 나누지 않으면 시스템은 매우 복잡해진다.
  • 왜냐하면, 객체가 하는 일에 변경이 있을 때에 해당 기능을 사용하는 부분을 모두 다시 테스트해야 하기 때문이다.
  • 그러므로, 한 객체가 단 하나의 일만 가지도록 분배하면 시스템에 변화가 생겨도 그 영향을 최소화할 수 있다.

Example

  • Calculator 객체는 덧셈, 뺄셈, 곱셈, 나눗셈만 할 수 있다. 👉 사칙연산에 대한 책임만 가지고 있다.
  • 이때, 계산기에 알람을 추가한다.
  • 만약, Calculator의 기능으로 alarm()을 추가하면 SRP에 위배된다.
  • 따라서, Calculator 객체 외에 Alarm 객체를 생성해줘야 한다.

SRP

OCP (Open-Closed Principle)

  • 개방-폐쇄 원칙
  • 확장에 대해서는 개방적, 변경에 대해서는 폐쇄적이어야 한다.
  • 기존 코드에 기능을 추가하되(open) 기존 코드는 변경하지 않는(close) 설계가 되어야 한다.
  • 여러 객체에서 사용하는 동일한 기능을 인터페이스에 정의하는 방법이 있다. 👉 캡슐화

Example

  • Animal 인터페이스를 구현하는 Dog, Cat, Bird 클래스는 cry()를 재정의한다.
  • 이처럼 캡슐화를 하면, 동물이 추가돼도 cry()를 호출하는 부분은 수정하지 않아도 쉽게 확장할 수 있다.

OCP

  • 클라이언트는 Animal 인터페이스에서 정의한 cry()만 호출하면 된다.
1
2
3
4
5
6
7
8
9
public class Client {
    public static void main(String args[]){
        Animal dog = new Dog();
        Animal cat = new Cat();
        
        dog.cry();
        cat.cry();
    }
}

LSP (Liskov Substitution Principle)

  • 리스코프 치환 원칙
  • 자식 클래스는 최소한 부모 클래스에서 가능한 행위를 수행할 수 있어야 한다.
  • 자식 클래스는 부모 클래스의 역할을 대체할 수 있어야 한다. 👉 클래스들의 행위는 일관된다.
  • 자식 클래스는 부모 클래스의 책임을 무시하거나 재정의하지 않고, 확장만 수행한다.
  • 오버라이드(재정의)는 가급적 피한다.

📚 우아한테크코스 SOLID 스터디에서 일부 발췌한 내용입니다.
💭 오버라이드를 못하면 상속의 의미가 없지 않나요?
💬 오버라이드 자체를 막는 게 아닙니다. 해당 책임이나 다른 예외처리를 하지 않는 게 문제예요.
부모 클래스의 책임과 예외처리를 해결하면, 오버라이드도 가능합니다.

ISP (Interface Segregation Principle)

  • 인터페이스 분리 원칙
  • 클래스는 자신이 사용하지 않는 인터페이스는 구현하지 말아야 한다.
  • 하나의 거대한 인터페이스보다 여러 개의 구체적인 인터페이스가 낫다.

SRP가 객체의 단일 책임을 뜻한다면, ISP는 인터페이스의 단일 책임을 뜻한다.

Example

  • Phone에는 call, text, alarm, calculate 기능이 있다.
  • 3GPhone4GPhone은 해당 기능들을 사용한다.
  • 만약, Phone 인터페이스에 해당 기능들을 정의하면 ISP에 위배된다.

ISP_NO

  • Phone 인터페이스에 모든 함수를 정의하지 않고, Call, Text, Alarm, Calculator 인터페이스에 각 함수를 정의하고 각 클래스에서 각각의 인터페이스를 구현하도록 설계하면 ISP를 만족한다.
  • 결국, 각 인터페이스의 각 메소드가 서로 영향을 미치지 않게 된다.
  • 따라서, 클래스는 자신이 사용하지 않는 메소드에 대해서 영향력이 줄어들게 된다.

ISP_YES

DIP (Dependency Inversion Principle)

  • 의존 역전 원칙
  • 의존 관계는 변화하기 어렵거나 변화가 거의 없는 것과 맺어야 한다.
  • 객체들이 서로 정보를 주고 받을 때 의존 관계가 형성되는데, 이때 객체들은 추상성이 낮은 클래스보다 추상성이 높은 클래스와 의존 관계를 맺어야 한다.
  • 일반적으로 인터페이스를 활용하면 이 원칙을 준수할 수 있다. 👉 캡슐화

Example

  • Client 객체는 Dog, Cat, Bird의 cry()에 직접 접근하지 않는다.
  • Animal 인터페이스의 cry()를 호출하면서 DIP를 만족한다.

DIP

References

SOLID

Cohesion, Coupling

This post is licensed under CC BY 4.0 by the author.