# Blog 5|設計模式 - 行為模式 - Reference : - https://shengyu7697.github.io/archives/ - https://www.jyt0532.com/ - https://refactoringguru.cn/design-patterns/catalog ## (1) 中介者模式 Mediator - 最經典的中介者模式應用場景之一就是 `聊天室` - 定義一個物件,負責協調其他多個物件之間的互動 - ![image](https://hackmd.io/_uploads/rkt2RLHk1e.png) ```c++= class ChatUser { private: std::string name; ChatRoom* chatRoom; public: ChatUser(const std::string& name, ChatRoom* room) : name(name), chatRoom(room) {} void send(const std::string& message) const { std::cout << name << " 發送消息: " << message << std::endl; chatRoom->sendMessage(this, message); } void receive(const std::string& message) { std::cout << name << " 收到消息: " << message << std::endl; } }; class ChatRoom{ private: std::vector<ChatUser*> chatUsers; public: void addUser(ChatUser* user) { chatUsers.push_back(user); } void sendMessage(const ChatUser* sender, const std::string& message) { for (ChatUser* user : chatUsers) { if (user != sender) { user->receive(message); } } } }; // client int main() { ChatRoom room; ChatUser Alice("Alice", &room); ChatUser Bob("Bob", &room); room.addUser(&Alice); room.addUser(&Bob); Alice.send("Hello, Bob!"); Bob.send("Hi, Alice!"); return 0; } ``` ## (2) 備忘錄模式 Memento - 讓我們保存某個物件的狀態,隨時恢復到之前的狀態 - ![image](https://hackmd.io/_uploads/HJIEyvSkye.png) ```c++= class History { public: void push(const string& s) { datas.push_back(s); } string pop() { if (datas.empty()) return string(""); string lastString = datas.back(); datas.pop_back(); return lastString; } private: std::vector<string> datas; }; // client int main() { string data; History history; data.append("123"); history.push(data); data.append("456"); history.push(data); data.append("789"); data = history.pop(); data = history.pop(); return 0; } ``` ## (3) 迭代器模式 Iterator - 遍歷一個集合,又不希望暴露它的內部結構,統一介面 - ![image](https://hackmd.io/_uploads/SyI_arBJyx.png) ```c++= class PlaylistIterator { private: std::vector<std::string> songs; int position; public: PlaylistIterator(const std::vector<std::string>& songs) : songs(songs), position(0) {} bool hasNext() override { return position < songs.size(); } std::string next() override { if (hasNext()) { return songs[position++]; } return ""; } }; // client std::vector<std::string> songs; auto it = new PlaylistIterator(songs); while (it->hasNext()) { std::cout << "Playing: " << it->next() << std::endl; } ``` ## (4) 解釋器模式 Interpreter - 如同編譯器,主要用來解析語言、處理簡單語法規則的 - ![image](https://hackmd.io/_uploads/S10O1wHkyl.png) ```c++= class Expression { public: virtual int interpret() = 0; virtual ~Expression() = default; }; class Number : public Expression { int number; public: Number(int num) : number(num) {} int interpret() override { return number; } }; class Add : public Expression { Expression *left, *right; public: Add(Expression* l, Expression* r) : left(l), right(r) {} int interpret() override { return left->interpret() + right->interpret(); } }; // client int main(void) { // std::string input = "4 + 3"; Expression* left = new Number(4); Expression* right = new Number(3); Expression* add = new Add(left, right); std::cout << add->interpret() << std::endl; delete left; delete right; delete add; return 0; } ``` ## (5) 策略模式 Strategy - ![image](https://hackmd.io/_uploads/rJd9pjfk1l.png) - 定義一系列的演算法,interface 提供方法介面,將實作封裝在 interface 的 subclass ```c++= class CompressionStrategy { virtual void compress(const std::string& filename) = 0; virtual ~CompressionStrategy() = default; }; class ZipStrategy : public CompressionStrategy { void compress(const std::string& filename) override { std::cout << "使用 ZIP 壓縮 " << filename << " 檔案\n"; } }; class RarStrategy : public CompressionStrategy { void compress(const std::string& filename) override { std::cout << "使用 RAR 壓縮 " << filename << " 檔案\n"; } }; class Compressor { private: std::unique_ptr<CompressionStrategy> strategy; public: // set strategy in constructor Compressor(std::unique_ptr<CompressionStrategy> strategy) : strategy(std::move(strategy)) {} // dependency injection void setStrategy(std::unique_ptr<CompressionStrategy> newStrategy) { strategy = std::move(newStrategy); } void compress(const std::string& filename) { strategy->compress(filename); } }; ``` ## (6) 狀態模式 State - 讓物件的行為與其狀態緊密相關,當狀態變化時,行為也會隨之變化 - 在狀態非常多的情況下,可能會讓程式變得繁瑣 - 切換狀態的過程中,可能會有一定的記憶體管理問題 - ![image](https://hackmd.io/_uploads/BkOfTHByJx.png) ```c++= class MusicPlayer { private: State* state; public: MusicPlayer(State* initState) : state(initState) {} ~MusicPlayer() { delete state; } void setState(State* newState) { delete state; state = newState; } void play() { state->play(this); } void pause() { state->pause(this); } void stop() { state->stop(this); } }; class State { public: virtual void play(MusicPlayer* player) = 0; virtual void pause(MusicPlayer* player) = 0; virtual void stop(MusicPlayer* player) = 0; }; class PlayingState : public State { public: void play(MusicPlayer* player) override; void pause(MusicPlayer* player) override; void stop(MusicPlayer* player) override; }; class PausedState : public State { public: void play(MusicPlayer* player) override; void pause(MusicPlayer* player) override; void stop(MusicPlayer* player) override; }; class StoppedState : public State { public: void play(MusicPlayer* player) override; void pause(MusicPlayer* player) override; void stop(MusicPlayer* player) override; }; ``` ## (7) 命令模式 Command - 它將 `Command` 封裝成一個物件 - ![image](https://hackmd.io/_uploads/rJacTBBkJl.png) ```c++= class Light { public: void turnOn() { std::cout << "Light is On\n"; } void turnOff() { std::cout << "Light is Off\n"; } }; class Command { public: virtual ~Command() = default; virtual void execute() = 0; }; class TurnOnCommand : public Command { public: explicit TurnOnCommand(Light* light) : light_(light) {} void execute() override { light_->turnOn(); } private: Light* light_; }; class TurnOffCommand : public Command { public: explicit TurnOffCommand(Light* light) : light_(light) {} void execute() override { light_->turnOff(); } private: Light* light_; }; ``` ## (8) 責任鏈模式 Chain Of Responsibility - 把任務逐層傳遞,直到某個物件可以處理它 - ![image](https://hackmd.io/_uploads/HJuK1IrJke.png) ```c++= class Handler { public: virtual void handleRequest(int request) {}; virtual Handler* setNext(Handler* handler) { nextHandler = handler; return handler; } protected: Handler* nextHandler = nullptr; }; class ConcreteHandler1 : public Handler { public: void handleRequest(int request) override { if (request < 10) { std::cout << "ConcreteHandler1 handled request: " << request << std::endl; } else if (nextHandler) { nextHandler->handleRequest(request); } else { std::cout << "Error" << std::endl; } } }; class ConcreteHandler2 : public Handler { public: void handleRequest(int request) override { if (request >= 10 && request < 20) { std::cout << "ConcreteHandler2 handled request: " << request << std::endl; } else if (nextHandler) { nextHandler->handleRequest(request); } else { std::cout << "Error" << std::endl; } } }; // client int main() { Handler* handler1 = new ConcreteHandler1(); Handler* handler2 = new ConcreteHandler2(); handler1->setNext(handler2); handler1->handleRequest(5); handler1->handleRequest(15); handler1->handleRequest(25); return 0; } ``` ```c++= ConcreteHandler1 handled request: 5 ConcreteHandler2 handled request: 15 Error ``` ## (9) 訪問者模式 Visitor - 在不改變物件結構的情況下,為物件添加新的操作,放在 visitor 上 - ![image](https://hackmd.io/_uploads/B19PyDr11x.png) ```c++= class Circle; class Rectangle; class Triangle; // Added class AreaCalculator { public: void visit(Circle* c); void visit(Rectangle* r); void visit(Triangle* t); int totalArea = 0; }; class Shape { public: virtual void accept(AreaCalculator* visitor) = 0; // Added virtual ~Shape() {} }; class Circle : public Shape { public: int R; Circle(int r) : R(r) {}; void accept(AreaCalculator* visitor) { // Added visitor->visit(this); } }; class Rectangle : public Shape { public: int W, H; Rectangle(int w, int h) : W(w), H(h) {}; void accept(AreaCalculator* visitor) { // Added visitor->visit(this); } }; class Triangle : public Shape { public: int B, H; Triangle(int b, int h) : B(b), H(h) {}; void accept(AreaCalculator* visitor) { // Added visitor->visit(this); } }; void AreaCalculator::visit(Circle* c) { // Added totalArea += 3.14 * c->R * c->R; } void AreaCalculator::visit(Rectangle* r) { // Added totalArea += r->W * r->H; } void AreaCalculator::visit(Triangle* t) { // Added totalArea += 0.5 * t->B * t->H; } // client int main() { std::vector<Shape*> shapes; shapes.push_back(new Circle(5)); shapes.push_back(new Rectangle(3, 4)); shapes.push_back(new Triangle(4, 5)); AreaCalculator areaCalculator; for (auto shape : shapes) { shape->accept(&areaCalculator); } std::cout << areaCalculator.totalArea << std::endl; return 0; } ``` ## (10) 模板方法模式 Template Method - 定義了一個演算法的骨架,而將一些通用步驟的實作延遲到子類中 - ![image](https://hackmd.io/_uploads/BJt818Hkye.png) ```c++= class AbstractClass { public: void TemplateMethod() const { Step1(); Step2(); Hook(); // Optional } void Step1() const { std::cout << "Step 1: Common implementation\n"; } virtual void Step2() const = 0; // pure virtual virtual void Hook() const {} // optional virtual ~AbstractClass() = default; }; class C1 : public AbstractClass { public: void Step2() const override { std::cout << "Step 2: C1\n"; } void Hook() const override { std::cout << "Hook: C1\n"; } }; class C2 : public AbstractClass { public: void Step2() const override { std::cout << "Step 2: C2\n"; } }; // client int main() { C1 obj1; obj1.TemplateMethod(); std::cout << "\n"; C2 obj2; obj2.TemplateMethod(); } ``` ```c++= Step 1: Common implementation Step 2: C1 Hook: C1 Step 1: Common implementation Step 2: C2 ``` ## (11) 觀察者模式 Observer - 當一個物件(被觀察者)發生改變時,所有依賴它的物件(觀察者)都會自動收到通知並更新自己 - notify 可以彈性決定要推播什麼訊息,最極端的就是兩種模式 push跟pull - push:把所有資料給你,不管你需不需要 - pull:只跟 observer 說我更新了,你需要什麼資料另外自己來看 - ![image](https://hackmd.io/_uploads/ByUclnzk1l.png) ```c++= class Subscriber { private: std::string name; public: Subscriber(const std::string& name) : name(name) {} void update(const std::string& videoTitle) { std::cout << name << " received notification: " << videoTitle << std::endl; } }; class YouTubeChannel { private: std::list<std::shared_ptr<Subscriber>> subscribers; std::string latestVideo; public: void addObserver(std::shared_ptr<Subscriber> subscriber) { subscribers.push_back(subscriber); } void removeObserver(std::shared_ptr<Subscriber> subscriber) { subscribers.remove(subscriber); } void notifyObservers() { for (const auto& observer : subscribers) observer->update(latestVideo); } void uploadNewVideo(const std::string& videoTitle) { latestVideo = videoTitle; notifyObservers(); } }; ```