2학년 디자인패턴 Behavioral Patterns
Chain of Responsibility(책임 연쇄 패턴)
-
명령 객체와 일련의 처리 객체를 포함하는 디자인 패턴
-
예시
-
UML
#include<iostream>
#include <list>
#include<string>
#include<vector>
using namespace std;
class Handler {
public:
virtual Handler* SetNext(Handler* handler) = 0;
virtual string Handle(string request) = 0;
};
class AbstractHandler : public Handler {
private:
Handler* next_handler_;
public:
AbstractHandler() : next_handler_(nullptr) { }
Handler* SetNext(Handler* handler) {
this->next_handler_ = handler;
return handler;
}
string Handle(string request) {
if (this->next_handler_) { return this->next_handler_->Handle(request); }
return {};
}
};
class AHandler : public AbstractHandler {
public:
string Handle(string request) override {
if (request == "A") { return "Request A Done"; }
else { return AbstractHandler::Handle(request); }
}
};
class BHandler : public AbstractHandler {
public:
string Handle(string request) override {
if (request == "B") { return "Request B Done"; }
else { return AbstractHandler::Handle(request); }
}
};
int main() {
AHandler* a = new AHandler;
BHandler* b = new BHandler;
a->SetNext(b);
vector<string> requests = { "B", "C", "A" };
for (const string& f : requests) {
const string result = a->Handle(f);
if (!result.empty()) {
cout << result << endl;
}
else {
cout << "Request " << f << " left untouched." << endl;
}
}
return 0;
}
-
장점
- 요청 처리 순서를 제어할 수 있다
- 단일 책임원칙, 개발/폐쇄 원칙
-
단점
- 일부 요청은 처리되지 않을 수 있다
Command
-
요청을 객체의 형태로 갭슐화하여 사용자가 보낸 요청을 나중에 이용할 수 있도록 메서드 이름, 매개변수, 등 요청에 필요한 정보를 저장 또는 로깅, 취소할 수 있게 하는 패턴이다
-
예시
-
UML
-
invoker = button, Receiver = product
#include<iostream>
#include <list>
#include<string>
#include<vector>
using namespace std;
class Receiver {
public:
void execute(const string& a) {
cout << a << " done" << endl;
}
};
class Command {
public:
virtual void Execute() const = 0;
};
class Command1 : public Command {
private:
Receiver* receiver_;
string a_;
public:
Command1(Receiver* receiver, string a) : receiver_(receiver), a_(a) { }
void Execute() const override { this->receiver_->execute(this->a_); }
};
class Command2 : public Command {
private:
Receiver* receiver_;
string a_;
string b_;
public:
Command2(Receiver* receiver, string a, string b) : receiver_(receiver), a_(a), b_(b) { }
void Execute() const override {
this->receiver_->execute(this->a_);
this->receiver_->execute(this->b_);
}
};
class Invoker {
private:
Command* c_;
public:
void setCommand(Command* command) {
this->c_ = command;
}
void Do() {
this->c_->Execute();
}
};
int main() {
Invoker* invoker = new Invoker;
Receiver* receiver = new Receiver;
invoker->setCommand(new Command1(receiver, "Americano"));
invoker->Do();
invoker->setCommand(new Command2(receiver, "Americano", "Mocha"));
invoker->Do();
return 0;
}
- 장점 : 실행취소, 다시실행, 지연된 작업실행 구현 가능
Iterator
-
반복자를 사용하여 컨테이너를 가로지르며 컨테이너의 요소들에 접근하는 디자인패턴
-
예시, UML
#include<iostream>
#include <list>
#include<string>
#include<vector>
using namespace std;
class Data
{
public:
Data(int
a = 0) : m_data_(
a) {}
void setData
(int
a) {
m_data_ =
a;
}
int getData() {
return m_data_;
}
private:
int m_data_;
};
typedef typename std::vector<Data>::iterator iter_type;
class Iterator {
public:
Iterator(vector<Data>* p_data) : m_p_data_(p_data) { First(); }
void First() { m_it_ = m_p_data_->begin(); }
void Next() { m_it_++; }
bool IsDone() { return (m_it_ == m_p_data_->end()); }
iter_type Current() { return m_it_; }
private:
vector<Data>* m_p_data_;
iter_type m_it_;
};
int main() {
vector<Data> cont;
Data a(100), b(1000), c(10000);
cont.push_back(a);
cont.push_back(b);
cont.push_back(c);
Iterator* it = new Iterator(&cont);
for (it->First(); !it->IsDone(); it->Next()) {
std::cout << it->Current()->getData() << std::endl;
}
return 0;
}
Mediator
-
중재자는 개체가 서로를 명시적으로 참조하지 않도록하여 느슨한 결합을 촉진합니다
-
예시
-
UML
-
Colleague1, Colleague2는 소통을 하려고 하지 않는다
#include<iostream>
#include <list>
#include<string>
#include<vector>
using namespace std;
class Mediator {
public:
virtual void Notify(BaseComponent* sender, string event) const = 0;
};
class BaseComponent {
protected:
Mediator* mediator_;
public:
BaseComponent(Mediator* mediator = nullptr) : mediator_(mediator) {
}
void set_mediator(Mediator* mediator) {
this->mediator_ = mediator;
}
};
class ConcreteMediator : public Mediator {
private:
Component1* component1_;
Component2* component2_;
public:
ConcreteMediator(Component1* c1, Component2* c2) : component1_(c1), component2_(c2) {
this->component1_->set_mediator(this);
this->component2_->set_mediator(this);
}
void Notify(BaseComponent* sender, std::string event) const override {
if (event == "A") { this->component1_->Msg(); }
if (event == "B") { this->component2_->Msg(); }
}
};
class Component1 : public BaseComponent {
public:
void DoA() { this->mediator_->Notify(this, "A"); }
void Msg() { cout << "Want to grab lunch?" << endl; }
};
class Component2 : public BaseComponent {
public:
void DoB() { this->mediator_->Notify(this, "B"); }
void Msg() { cout << "No" << endl; }
};
int main() {
Component1* c1 = new Component1;
Component2* c2 = new Component2;
ConcreteMediator* mediator = new ConcreteMediator(c1, c2);
c1->DoA();
c2->DoB();
delete c1;
delete c2;
delete mediator;
return 0;
}
- 장점
- 프로그램의 다양한 구성 요소간의 결합을 줄일 수 있다
- 개별 컴포넌트를 보다 쉽게 재사용할 수 있다
- 단점
- 시간이 지남에 따라 mediator는 신의 대상으로 될 수 있다
Memento
-
객체를 이전상태로 되돌릴수 있는 기능을 제공 -> version control system(git)
-
예시
-
UML
-
originator -> 수정
-
memento -> state
-
caretaker -> memento manager
#include<iostream>
#include <list>
#include<string>
#include<vector>
#include<cstdlib>
#include<ctime>
using namespace std;
class Memento {
public:
virtual int getState() const = 0;
};
class ConcreteMemento : public Memento {
private:
int state_;
public:
ConcreteMemento(int state) : state_(state) {
this->state_ = state;
}
int getState() const {
return this->state_;
}
};
class Originator {
private:
int state_;
public:
Originator(int state) : state_(state) {
cout << "Current State: " << this->state_ << endl;
}
void DoSomething() {
this->state_ += rand() % 10;
cout << "State has been changed to: " << this->state_ << endl;
}
Memento* Save() {
return new ConcreteMemento(this->state_);
}
void Restore(Memento* memento) {
this->state_ = memento->getState();
cout << "State has been changed to: " << this->state_ << endl;
}
};
class Caretaker {
private:
std::vector <Memento*> mementos_;
Originator* originator_;
public:
Caretaker(Originator* originator) : originator_(originator) {
this->originator_ = originator;
}
void Backup() {
cout << "Saving state...\n";
this->mementos_.push_back(this->originator_->Save());
}
void Undo() {
if (!this->mementos_.size()) {
return;
}
Memento* memento = this->mementos_.back();
this->mementos_.pop_back();
std::cout << "Restoring state to: " << memento->getState() << endl;
try {
this->originator_->Restore(memento);
}
catch (...) {
this->Undo();
}
}
};
int main() {
srand(static_cast<unsigned int>(std::time(NULL)));
Originator* originator = new Originator(0);
Caretaker* caretaker = new Caretaker(originator);
caretaker->Backup();
originator->DoSomething();
caretaker->Backup();
originator->DoSomething();
caretaker->Undo();
caretaker->Undo();
return 0;
}
- 장점
- 객체상태의 스냅샷을 생성할 수 있다
- 단점
- memento를 자주 생성하면 RAM을 많이 소모할 수 있다
- 수명 주기를 추적해야 한다
Observer
-
객체의 상태변화가 있을 때마다 메서드, 등을 통해 객체가 직접 목록의 각 옵저버에게 통지하는 패턴
-
예시
-
UML
#include<iostream>
#include <list>
#include<string>
#include<vector>
#include<cstdlib>
#include<ctime>
using namespace std;
class IObserver {
public:
virtual void Update(const string& message_from_subject) = 0;
};
class Observer : public IObserver {
public:
Observer(Subject& subject) : subject_(subject) {
this->subject_.Attach(this);
}
void Update(const std::string& message_from_subject) override {
message_from_subject_ = message_from_subject;
PrintInfo();
}
void PrintInfo() {
cout << "Observer: New message arrived --> [ " << this->message_from_subject_ << " ]" << endl;
}
private:
std::string message_from_subject_;
Subject& subject_;
};
class ISubject {
public:
virtual void Attach(IObserver* observer) = 0;
virtual void Notify() = 0;
};
class Subject : public ISubject {
public:
void Attach(IObserver* observer) { list_observer_.push_back(observer); }
void Notify() {
std::list<IObserver*>::iterator iterator = list_observer_.begin();
while (iterator != list_observer_.end()) {
(*iterator)->Update(message_);
++iterator;
}
}
void CreateMessage(string message) {
this->message_ = message;
Notify();
}
private:
list<IObserver*> list_observer_;
string message_;
};
int main() {
Subject* subject = new Subject;
Observer* observer1 = new Observer(*subject);
Observer* observer2 = new Observer(*subject);
Observer* observer3 = new Observer(*subject);
subject->CreateMessage("New Drink Available");
return 0;
}
장점 : 객체 간의 관계를 런타임에 설정할 수 있다
단점 : 무작위로 통보된다
State
-
객체의 내부 상태가 변경될 때 객체가 동작을 변경하도록 허용합니다. 개체는 클래스를 변경하는 것으로 나타난다
-
예시
-
UML
#include<iostream>
#include <list>
#include<string>
#include<vector>
#include<cstdlib>
#include<ctime>
using namespace std;
class Context;
class State {
protected:
Context* context_;
public:
void set_context(Context* context) {
this->context_ = context;
}
virtual void Handle1() = 0;
};
class Context{
private:
State* state_;
public:
Context(State* state) : state_(nullptr) {
this-> TransitionTo(state);
}
~Context() { delete state_; }
void TransitionTo(State* state) {
if(this->state_ != nullptr)
delete this->state_;
this->state_ = state;
this->state_->set_context(this);
}
void Request1() {
this->state_->Handle1();
}
};
class ReadyState : public State {
public: void Handle1();
};
class RunState : public State {
public: void Handle1();
};
class OffState : public State {
public: void Handle1();
};
void ReadyState::Handle1() {
cout << "[Ready State] --> [Run State]" << endl;
this->context_->TransitionTo(new RunState);
};
void RunState::Handle1() {
cout << "[Run State] --> [Off State]" << endl;
this->context_->TransitionTo(new OffState);
};
void OffState::Handle1() {
cout << "[Off State] --> [Ready State]" << endl;
this->context_->TransitionTo(new ReadyState);
};
int main() {
Context* context = new Context(new OffState);
context->Request1();
context->Request1();
context->Request1();
return 0;
}
- 장점 : 큰 상태 머신 조건을 제거하여 컨텍스트의 코드를 단순화합니다.
- 단점 : 상태 머신에 몇 가지 상태만 있거나 거의 변경되지 않는 경우 패턴을 적용하는 것이 과도할 수 있습니다.
Strategy
-
특정한 계열의 알고리즘들을 정의하고 각 알고리즘을 캡슐화하여 이 알고리즘들을 해당 계열 안에서 상호 교체가 가능하게 만든다
-
예시
-
UML
#include<iostream>
#include <list>
#include<string>
#include<vector>
#include<cstdlib>
#include<ctime>
using namespace std;
class Strategy {
public:
virtual void doAlgorithm() const = 0;
};
class ConcreteStrategyA : public Strategy {
public:
void doAlgorithm() const {
cout << "Algorithm A is applied" << endl;
}
};
class ConcreteStrategyB : public Strategy {
void doAlgorithm() const {
cout << "Algorithm B is applied" << endl;
}
};
class Context
{
private:
Strategy* strategy_;
public:
Context(Strategy* strategy = nullptr) : strategy_(strategy) { }
void set_strategy(Strategy* strategy)
{
delete this->strategy_;
this->strategy_ = strategy;
}
void makeCoffee() const
{
this->strategy_->doAlgorithm();
}
};
int main()
{
Context* context = new Context(new ConcreteStrategyA);
context->makeCoffee();
context->set_strategy(new ConcreteStrategyB);
context->makeCoffee();
return 0;
}
state pattern이랑 유사
-
장점
- 런타임에 객체 내부에서 사용되는 알고리즘을 교환할 수 있습니다.
- 알고리즘을 사용하는 코드에서 알고리즘의 구현 세부 정보를 분리할 수 있습니다.
-
단점
- 알고리즘이 몇 개만 있고 거의 변경되지 않는다면 새 클래스와 인터페이스로 프로그램을 지나치게 복잡하게 만들 이유가 없습니다.
Template Method
-
작업에서 알고리즘의 골격을 정의하고 일부 단계를 하위 클래스로 연기합니다.
-
예시
-
UML
Template Method -> factory method, abstract factory의 근본이 된다
class AbstractClass
{
public:
void TemplateMethod() {
this
->BaseOperation1();
this
->RequiredOperation1();
this
->BaseOperation2();
this
->RequiredOperation2();
}
protected:
void BaseOperation1() const
{
cout << "Base Operation 1" << endl;
}
void BaseOperation2() const
{
cout << "Base Operation 2" << endl;
}
virtual void RequiredOperation1() const = 0;
virtual void RequiredOperation2() const = 0;
};
장점 : 클라이언트가 큰 알고리즘의 특정 부분만 재정의하도록 하여 알고리즘의 다른 부분에서 발생하는 변경 사항의 영향을 덜 받도록 할 수 있습니다.
단점 : 일부 클라이언트는 제공된 알고리즘의 골격에 의해 제한될 수 있습니다.
visitor
-
알고리즘을 객체 구조에서 분리시키는 디자인 패턴이다.
-
클래스에서 data영역과 function영역을 나눈다
-
예시
-
UML
#include<iostream>
#include <list>
#include<string>
#include<vector>
#include<cstdlib>
#include<ctime>
using namespace std;
class Component{
public:
virtual void Accept(Visitor* visitor) = 0;
};
class Americano : public Component{
public:
void Accept(Visitor* visitor) {
visitor -> VisitConcreteComponentA(this);
}
};
class Latte : public Component{
public:
void Accept(Visitor* visitor) {
visitor -> VisitConcreteComponentB(this);
}
};
class Visitor {
public:
virtual void VisitConcreteComponentA(const Americano* element) = 0;
virtual void VisitConcreteComponentB(const Latte* element) = 0;
};
class ConcreteVisitor1 : public Visitor {
public:
void VisitConcreteComponentA(const Americano* element) {
cout << "Americano" << endl;
}
void VisitConcreteComponentB(const Latte* element) {
cout << "Latte" << endl;
}
};
int main() {
Component* arr[2] = { new Americano, new Latte };
ConcreteVisitor1* visitor1 = new ConcreteVisitor1;
for (int i = 0; i < 2; i++) {
arr[i]->Accept(visitor1);
}
return 0;
}
단점 : 방문자는 개인 필드 및 작업해야 하는 요소의 메서드에 대한 필수 액세스 권한이 부족할 수 있다.
Author And Source
이 문제에 관하여(2학년 디자인패턴 Behavioral Patterns), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@sgh9702/2학년-디자인패턴-Behavioral-Patterns저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)