---
# System prepended metadata

title: Blog 5｜設計模式 - 行為模式
tags: [部落格]

---

# 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();
    }
};
```
