SOLID 디자인 원칙
SOLID는 다음과 같은 디자인 원칙들을 아우르는 약어이다.
- 단일 책임 원칙(Single Responsibilty Principle, SRP)
- 열림-닫힘 원칙(Open-Closed Principle, OCP)
- 리스코프 치환 원칙(Liskov Substitution Principle, LSP)
- 인터페이스 분리 원칙(Interface Segregation Principle, ISP)
- 의존성 역전 원칙(Dependency Inversion Principle, DIP)
인터페이스 원칙(Interface Segregation Principle, ISP)
복합기능 프린터를 만들기로 했다고 하자. 이 프린터는 프린트, 스캔, 팩스 기능이 합쳐져 있다. 따라서 다음과 같이 프린터를 정의한다.
struct MyFavouritePrinter : IMachine
{
void print(vector<Document*> docs) override;
void fax(vector<Document*> docs) override;
void scan(vector<Document*> docs) override;
}
여기까지는 나쁠 것이 없다. 이제 프린터의 구현을 하청 업체에 맡기려 한다. 하청 업체는 여러 곳이 될 수 있고, 각기 제품 라인업에 따라 기능 조합을 달리 할 수 있다고 하자. 각 업체가 복합 기능 프린터를 구현할 수 있도록 아래와 같이 인터페이스를 추출한다. (많은 IDE가 클래스의 인터페이스를 자동으로 추출하는 기능을 제공한다.)
struct IMachine
{
virtual void print(vector<Document*> docs) = 0;
virtual void fax(vector<Document*> docs) = 0;
virtual void scan(vector<Document*> docs) = 0;
}
여기서 문제가 발생한다. 어떤 업체는 스캔 기능이나 팩스 기능이 필요하지 않을 수 있다. 단지 프린터만 만들고 싶을 수 있다. 하지만 이 인터페이스는 모든 기능을 구현하도록 강제한다. 물론 업체에서 빈 함수를 만들어서 대응할 수도 있다. 그럼 무엇이 문제일까?
인터페이스 분리 원칙이 의미하는 바는 필요에 따라 구현할 대상은 선별할 수 있도록 인터페이스를 별개로 두어야 한다는 것이다. 프린트와 스캔은 서로 다른 동작이므로(예를 들어 스캐너는 프린트를 할 수 없다.) 인터페이스를 구분하여 나눈다.
struct IPrinter
{
virtual void print(vector<Document*> doc) = 0;
}
struct IScanner
{
virtual void scan(vector<Document*> doc) = 0;
}
이제 프린터와 스캐너를 기능적인 필요에 따라서 따로따로 구현할 수 있다.
struct Printer : IPrinter
{
void print(vector<Document*> docs) override;
}
struct scanner : IScanner
{
void scan(vector<Document*> docs) override;
}
그러면, 복합기 전체를 나타내는 IMachine 인터페이스는 어떻게 되나? 아래와 같이 아서의 인터페이스를 조합하여 만들수 있다.
struct IMachine : Printer, IScanner
{
}
이 인터페이스로 복합기를 구현한다. 예를 들어 다음과 같이 아무 인터페이스 IPrinter와 IScanner의 구현을 재활용하여 각각에 동작을 위임하는 방식으로 구현할 수도 있다.
struct Machine : IMachine
{
IPrinter& printer;
IScanner& scanner;
Machine(IPrinter& printer, IScanner& scanner)
:printer(printer), scanner(scanner)
{
}
void print(vector<Document*> docs) override
{
printer.print(docs);
}
void scan(vector<Document*> docs) override
{
scanner.scan(docs);
}
}
다시 한번 이 아이디어를 정리하자면, 한 덩어리의 복잡한 인터페이스를 목적에 따라 구분하여 나눔으로써, 인터페이스 모든 항목에 대한 구현을 강제하지 않고 실제 필요한 인터페이스만 구현할 수 있도록 하는 것이다. 만약 어떤 애플리케이션의 플러그인 모듈을 개발할 때 뭐가 뭔지 알 수 없는 혼란스럽기만 한 수십 개의 함수를 빈 껍데기 또는 null 리턴으로 구현하고 있다면 그 애플리케이션의 플러그인 인터페이스 설계자가 인터페이스 분리 원칙을 위반한 것이다.
출처
'프로그래밍' 카테고리의 다른 글
디자인 패턴이란? (1) | 2023.11.29 |
---|---|
SOLID 디자인 원칙 - 의존성 역전 원칙 (2) | 2023.11.21 |
SOLID 디자인 원칙 - 리스코프 치환 원칙 (2) | 2023.11.20 |
Tortoise SVN externals 설정하기 (0) | 2023.11.15 |
SOLID 디자인 원칙 - 단일 책임 원칙 (0) | 2023.11.15 |
댓글