JUINTINATION
좋은 객체 지향 설계의 5가지 원칙 (SOLID) 본문
반응형
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