🎈 체인 패턴 (Chain of Responsibility)
• 책임 전가(고리, 연쇄)라고 불리는 패턴
• 처리할 수 있는 다른 객체 연결
→ 요청을 처리하거나 못해도 다음 객체로 전달한다.
• 요청 자체와 각 처리 객체 사이의 결합을 피한다.
→ 객체 메시지 송신과 수신 분리
• 하나의 객체에서 모든 처리를 구현하지 않을 수 있다.
→ 객체의 의존성 주입을 통한 위임
• 디자인 패턴에서 행위 패턴에 속한다.
[체인 패턴 예제]
#include <iostream>
struct Handler
{
Handler* next = nullptr;
void Handle(int issue)
{
// 처리 가능한 경우
if (Process(issue))
{
std::cout << " ok" << std::endl;
return;
}
// 처리하지 못하고, 다음 처리 객체가 남아 있는 경우
if (next != nullptr)
next->Handle(issue);
}
virtual bool Process(int problem) = 0;
};
class MinusHandler : public Handler
{
public:
bool Process(int issue) override
{
std::cout << " Minus Handler -";
bool b = issue < 0; // 음수
return b;
}
};
class OddHandler : public Handler
{
public:
bool Process(int problem) override
{
std::cout << " Odd Handler -";
bool ret = problem % 2 == 1; // 홀수
return ret;
}
};
class EvenHandler : public Handler
{
public:
bool Process(int problem) override
{
std::cout << " Even Handler -";
bool ret = problem % 2 == 0; // 짝수
return ret;
}
};
int main()
{
MinusHandler minusHandle;
EvenHandler evenHandle;
OddHandler oddHandle;
minusHandle.next = &evenHandle;
evenHandle.next = &oddHandle;
oddHandle.next = nullptr;
minusHandle.Handle(-2);
minusHandle.Handle(10);
minusHandle.Handle(13);
}
Handler 역할로 Minus, Odd, Even 객체를 만들고 연결해주었다.
- 시작하는 Handler를 설정 가능
- 우선순위에 따라 처리되는 Handler 순서 설정 가능
- 새로운 Handler 객체를 만들어 연결 해주면 된다.
[예제 코드]
책임 연쇄 패턴 적용 X → 조건문 활용
#include <iostream>
#include <string>
class Logger {
public:
Logger(int level) : level(level) {}
void log(const std::string& message) {
if (level == 1 && Logger::logLevel >= 1) {
std::cout << "Error Logger: " << message << std::endl;
}
if (level == 2 && Logger::logLevel >= 2) {
std::cout << "File Logger: " << message << std::endl;
}
if (level == 3 && Logger::logLevel >= 3) {
std::cout << "Console Logger: " << message << std::endl;
}
}
static void setLogLevel(int level) {
logLevel = level;
}
private:
int level;
static int logLevel;
};
int Logger::logLevel = 3;
void main() {
Logger::setLogLevel(3);
Logger errorLogger(1);
Logger fileLogger(2);
Logger consoleLogger(3);
errorLogger.log("This is an error message.");
fileLogger.log("This is a file log message.");
consoleLogger.log("This is a console log message.");
}
책임 연쇄를 통해 각 Logger에 Error Message를 출력할 수 있다.
각 객체가 단일 책임을 가지게 되었는데
새로운 Logger 유형 추가된다면 수정하기 쉬워졌다.
🎈 체인 패턴 장단점
+ 연쇄 구성에 따라 동적으로 처리자 변경/확장할 때 유용
+ 객체간 느슨한 결합 (객체 변경시 영향도 최소화)
+ 단일 책임 원칙 준수: 각 처리자는 하나의 책임을 갖도록 설계
- 순차적으로 객체를 처리하기 다소 지연 시간 발생
- 요청이 처리 과정을 따라가지 않을 수 있으며 예측하기 어려울 수 있음
- 적절한 종료 조건이 없으면 무한 Loop에 빠질 수 있음
🎈 활용 예시
Event 처리 시스템: GUI 라이브러리나 WEB 이벤트 처리와 같이 다양한 이벤트 Handler 처리
Log 처리: 각 로그 메시지의 유형에 따른 처리
보안 시스템: 보안 규칙에 따른 권한 부여
미들웨어: 서버에 도달하기 전에 다양한 처리 가능
[예제 코드]
ATM에서 지출 요청을 처리
#include <iostream>
#include <string>
class Request {
public:
Request(int amount) : amount(amount) {}
int getAmount() const { return amount; }
void setAmount(int _amount) { amount = _amount; }
private:
int amount;
};
class Handler {
public:
Handler(Handler* successor = nullptr) : successor(successor) {}
virtual void handleRequest(Request& request) {
if (successor) {
successor->handleRequest(request);
}
}
void setSuccessor(Handler* successor) {
this->successor = successor;
}
protected:
Handler* successor;
};
class Company_B : public Handler {
public:
Company_B(Handler* successor = nullptr) : Handler(successor) {}
void handleRequest(Request& request) override {
std::cout << "[B]: OK! " << request.getAmount() << std::endl;
}
};
class Company_A : public Handler {
public:
Company_A(Handler* successor = nullptr) : Handler(successor) {}
void handleRequest(Request& request) override {
if (request.getAmount() <= 100) {
std::cout << "[A]: OK! " << request.getAmount() << std::endl;
}
else {
std::cout << "[A]: Sorry... -> ";
successor->handleRequest(request);
}
}
};
void main() {
Company_B objB;
Company_A objA;
objA.setSuccessor(&objB);
Request request(90);
objA.handleRequest(request);
request.setAmount(150);
objA.handleRequest(request);
}
amount 100 이하는 Company_A에서 처리할 수 있지만
그것보다 많은 수량은 Company_B에서 처리 가능하다.
🎈 다른 디자인 패턴과 비교
객체를 감싸서 객체에 새로운 기능을
동적으로 추가하는데 사용되는 패턴
• 전략 (Strategy)
특정 행동(알고리즘)을 캡슐화하고, 런타임에 알고리즘 교체
객체의 동작을 변경하려는 경우 사용
명령을 캡슐화하고 수신자에 전달하여 실행
요청과 실행을 분리하려는 경우에 사용된다.
객체 상태가 변경될 때 관찰자에게 알려주면
그 변화를 다수의 객체에게 알리고자할 때 사용
'까망 동네 > 디자인 패턴' 카테고리의 다른 글
[디자인패턴] 어댑터 패턴 (Adapter Pattern) (8) | 2024.10.13 |
---|---|
💻 디자인 패턴(Design Pattern)이란? (6) | 2024.08.02 |
객체 지향 프로그래밍 5대 원칙 [SOLID] (40) | 2023.11.19 |
[디자인 패턴] 반복자 패턴(Iterator Pattern) (33) | 2023.11.14 |
[디자인패턴] 방문자 패턴 (Visitor Pattern) (3) | 2023.11.13 |
댓글