JUINTINATION

좋은 객체 지향 설계의 5가지 원칙 (SOLID) 본문

JAVA객체지향디자인패턴

좋은 객체 지향 설계의 5가지 원칙 (SOLID)

DEOKJAE KWON 2023. 7. 2. 22:31
반응형

SRP: 단일 책임 원칙(Single Responsibility Principle)

  • 한 클래스는 하나의 책임만 가져야 한다.
  • 하나의 책임이라는 것은 모호하다.
    • 클 수 있고, 작을 수 있다.
    • 문맥과 상황에 따라 다르다.
  • 객체 지향 설계 관점에서 책임의 기본 단위는 객체이다.
    • 책임 = 해야하는 것, 할 수 있는 것, 변경 이유
  • 중요한 기준은 변경이다. 요구 사항의 변경이 있을 때 가능한 영향을 받는 부분을 줄여야 한다.
    • 변경 사유가 될 수 있는 것을 하나로 만들어야 한다. 
      • ex) DB 스키마 변경, UI 변경, 객체의 생성과 사용을 분리 

OCP: 개방-폐쇄 원칙 (Open/Closed Principle)

  • 소프트웨어 요소는 확장에는 열려 있으나 변경에는 닫혀 있어야 한다
  • 기존의 코드는 변경하지 않으면서 기능을 추가할 수 있도록 설계해야 한다.

  • 왼쪽 클래스 다이어그램처럼 설계를 했을 때 도서관 대여 명부가 추가되면 Client의 코드를 수정해야 한다.
  • 따라서 오른쪽 클래스 다이어그램처럼 OCP를 만족하는 설계를 해야 한다.

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

  • 프로그램의 객체는 프로그램의 정확성을 깨뜨리지 않으면서 하위 타입의 인스턴스로 바꿀 수 있어야 한다.
    • ex) 자동차 인터페이스의 엑셀은 앞으로 가라는 기능, 뒤로 가게 구현하면 LSP 위반
    • 즉, 부모 클래스와 자식 클래스 사이는 행위가 일관되어야 한다.
      • 자식 is kind of 부모
  • 인터페이스를 구현한 구현체는 믿고 사용하려면, 이 원칙이 필요하다.
class Bag {
    private int price;
    public void setPrice(int price) {
        this.price = price;
    }
    public int getPrice() {
        return price;
    }
}
class DiscountBag extends Bag {
    private double discountRate;
    public void setDiscountRate(int discountRate) {
        this.discountRate = discountRate;
    }
    /*
    @Override
    public void setPrice(int price) {
        // Bag의 setPrice를 재정의하여 기능을 수정하게 된다면 LSP를 위반한다.
        super.setPrice((1 - (int) discountRate) * price);
    }
    */
    // LSP를 준수하기 위해 setPrice 메서드를 재정의하지 않고 아래와 같은 메서드를 추가할 수 있다.
    public void applyDiscount(int price) {
        super.setPrice((1 - (int) discountRate) * price);
    }
}

ISP: 인터페이스 분리 원칙 (Interface segregation principle)

  • 인터페이스를 클라이언트에 특화되도록 분리시켜야 한다.
    • 클라이언트는 자신이 사용하지 않는 기능에는 영향을 받지 않는다.
  • 특정 클라이언트를 위한 인터페이스 여러 개가 범용 인터페이스 하나보다 낫다.
    • 자동차 인터페이스 -> 운전 인터페이스, 정비 인터페이스로 분리
    • 사용자 클라이언트 -> 운전자 클라이언트, 정비사 클라이언트로 분리
      • 분리하면 정비 인터페이스 자체가 변해도 운전자 클라이언트에 영향을 주지 않음
      • 인터페이스가 명확해지고, 대체 가능성이 높아진다.

  • 왼쪽 클래스 다이어그램처럼 설계를 했을 때 프린트 클라이언트가 복사 기능을 사용하는 등 문제가 발생할 수 있다.
  • 따라서 오른쪽 클래스 다이어그램처럼 LSP를 만족하는 설계를 해야 한다. 코드로 나타내면 다음과 같다.
interface Printer {
    void print();
}
interface CopyMachine {
    void copy();
}
interface Fax {
    void fax();
}
class CompoundMachine implements Printer, CopyMachine, Fax {
    @Override
    public void print() {
        System.out.println("CompoundMachine.print");
    }
    @Override
    public void copy() {
        System.out.println("CompoundMachine.copy");
    }
    @Override
    public void fax() {
        System.out.println("CompoundMachine.fax");
    }
}
class PrinterClient {
    public void use(Printer p) {
        p.print();
    }
}
class CopyClient {
    public void use(CopyMachine c) {
        c.copy();
    }
}
class FaxClient {
    public void use(Fax f) {
        f.fax();
    }
}
public class Main {
    public static void main(String[] args) {
        PrinterClient printerClient = new PrinterClient();
        Printer p = new CompoundMachine();
        printerClient.use(p);
        
        CopyClient copyClient = new CopyClient();
        CopyMachine c = new CompoundMachine();
        copyClient.use(c);
        
        FaxClient faxClient = new FaxClient();
        Fax f = new CompoundMachine();
        faxClient.use(f);
    }
}

DIP: 의존관계 역전 원칙 (Dependency inversion principle)

  • 의존관계를 맺을 때 변화하기 쉬운 것보다 변화하기 어려운 것에 의존해야 한다.
    • 구현 클래스에 의존하지 말고, 인터페이스에 의존하라는 뜻
728x90

'JAVA객체지향디자인패턴' 카테고리의 다른 글

스테이트(State) 패턴  (0) 2023.07.06
스트래티지(Strategy) 패턴  (0) 2023.07.06
싱글턴(Singleton) 패턴  (0) 2023.07.05
디자인패턴 개요  (0) 2023.07.03
객체 지향 원리  (0) 2023.07.02
Comments