---
# System prepended metadata

title: Blog 4｜設計模式 - 結構型模式
tags: [部落格]

---

# Blog 4｜設計模式 - 結構型模式

- Reference :
    - https://shengyu7697.github.io/archives/
    - https://www.jyt0532.com/
    - https://refactoringguru.cn/design-patterns/catalog
    
## (1) 適配器模式 Adapter
- ![image](https://hackmd.io/_uploads/Hk2XYcfJ1x.png)
- 想要重用某個已經存在的類，但其接口與目前系統不匹配的情況下。`Adapter` 模式允許你創建一個中間層，使新系統和舊接口之間進行協作。
```c++=

// 已存在舊的實作
class OldRectangle {
public:
    void draw(int x1, int y1, int x2, int y2) {
        std::cout << "OldRectangle: (" << x1 << ", " << y1 << "), (" << x2 << ", " << y2 << ")\n";
    }
};

// 新的接口
class NewRectangleInterface {
public:
    virtual void drawRectangle(int x, int y, int width, int height) = 0;
    virtual ~NewRectangleInterface() = default;
};

// Adapter
class RectangleAdapter : public NewRectangleInterface {
private:
    OldRectangle* oldRectangle;
public:
    RectangleAdapter(OldRectangle* rectangle) : oldRectangle(rectangle) {}

    void drawRectangle(int x, int y, int width, int height) override {
        oldRectangle->draw(x, y, x + width, y + height);
    }
};

// client
int main() {
    OldRectangle* oldRectangle = new OldRectangle();
    NewRectangleInterface* adapter = new RectangleAdapter(oldRectangle);
    
    adapter->drawRectangle(10, 20, 30, 40);
    
    delete oldRectangle;
    delete adapter;
    return 0;
}
```
```c++=
OldRectangle: (10, 20), (40, 60)
```

## (2) 橋接模式 Bridge
- Bridge Pattern 的關鍵點：
    - 抽象部分（Abstraction）：定義一個接口，並維護一個指向實現部分的引用。
    - 實現部分（Implementor）：定義具體的行為，並可以有不同的具體實現類別。
- ![image](https://hackmd.io/_uploads/B1QuwXNJye.png)
```c++=
// Implementor 實現部分
class Color {
public:
    virtual void applyColor() = 0;  // 純虛函數，子類別需要具體實現
    virtual ~Color() = default;     // 虛擬析構函數
};

class RedColor : public Color {
public:
    void applyColor() override {
        std::cout << "Applying Red color." << std::endl;
    }
};

class BlueColor : public Color {
public:
    void applyColor() override {
        std::cout << "Applying Blue color." << std::endl;
    }
};

// Abstraction 抽象部分
class Shape {
protected:
    std::shared_ptr<Color> color;  // 持有 Implementor 的引用
public:
    Shape(std::shared_ptr<Color> c) : color(c) {}
    virtual void draw() = 0;
    virtual ~Shape() = default;
};

class Circle : public Shape {
public:
    Circle(std::shared_ptr<Color> c) : Shape(c) {}

    void draw() override {
        std::cout << "Drawing a Circle. ";
        color->applyColor();  // 使用實現部分
    }
};

class Square : public Shape {
public:
    Square(std::shared_ptr<Color> c) : Shape(c) {}

    void draw() override {
        std::cout << "Drawing a Square. ";
        color->applyColor();  // 使用實現部分
    }
};

int main() {
    // 具體實現類別
    std::shared_ptr<Color> red = std::make_shared<RedColor>();
    std::shared_ptr<Color> blue = std::make_shared<BlueColor>();

    // 使用不同的實現來創建形狀
    std::shared_ptr<Shape> circle = std::make_shared<Circle>(red);
    std::shared_ptr<Shape> square = std::make_shared<Square>(blue);

    circle->draw();  // 繪製紅色的圓
    square->draw();  // 繪製藍色的正方形

    return 0;
}
```
```c++=
Drawing a Circle. Applying Red color.
Drawing a Square. Applying Blue color.
```

## (3) 組合模式 Composite
- Component：為所有具體元件和組合類定義共同介面。
    - Leaf：表示組合中的葉節點對象，葉節點沒有子節點。
    - Composite：表示複雜元件，包含其他元件（可以是葉節點或其他複雜元件）。
- 讓樹狀結構中的物件與物件集合能被同樣對待
- ![image](https://hackmd.io/_uploads/S1F2X9fJ1l.png)
```c++=
class Graphic {
public:
    virtual void draw() const = 0;
    virtual ~Graphic() = default;
};

class Circle : public Graphic {
public:
    void draw() const override {
        std::cout << "Drawing Circle\n";
    }
};

class Rectangle : public Graphic {
public:
    void draw() const override {
        std::cout << "Drawing Rectangle\n";
    }
};

class CompositeGraphic : public Graphic {
private:
    std::vector<Graphic*> graphics;
public:
    void add(Graphic* graphic) {
        graphics.push_back(graphic);
    }

    void draw() const override {
        for (const auto& graphic : graphics) {
            graphic->draw();
        }
    }
};

// client
int main() {
    // 單一圖形
    Circle circle;
    Rectangle rectangle;

    // 組合圖形
    CompositeGraphic composite, complexComposite;
    composite.add(&circle);
    composite.add(&rectangle);
    complexComposite.add(&composite);
    complexComposite.add(&circle);

    std::cout << "Drawing composite graphic:\n";
    complexComposite.draw();
    return 0;
}
```
```c++=
Drawing composite graphic:
Drawing Circle
Drawing Rectangle
Drawing Circle
```

## (4) 裝飾器模型 Decorator
- 裝飾者模式是一種結構型設計模式，它允許你動態地為物件添加功能，而不需要修改其原有的程式碼。
- 符合開放封閉原則，也就是添加新的裝飾者不需要修改原始類別
- 符合單一職責原則，即每個裝飾者只負責一項特定的功能增強。
- [理解Python 裝飾器看這篇就夠了](https://foofish.net/python-decorator.html)
- ![image](https://hackmd.io/_uploads/BJofE9G11x.png)

```c++=
class Pizza {
public:
    virtual std::string getDescription() const {
        return "pizza";
    }
    virtual int cost() const {
        return 100;
    }
    virtual ~Pizza() = default;
};

class CheeseDecorator : public Pizza {
private:
    std::unique_ptr<Pizza> pizza;
public:
    CheeseDecorator(std::unique_ptr<Pizza> p) : pizza(std::move(p)) {}
    std::string getDescription() const override {
        return pizza->getDescription() + " + cheese";
    }
    int cost() const override {
        return pizza->cost() + 10;
    }
};

class BaconDecorator : public Pizza {
private:
    std::unique_ptr<Pizza> pizza;
public:
    BaconDecorator(std::unique_ptr<Pizza> p) : pizza(std::move(p)) {}
    std::string getDescription() const override {
        return pizza->getDescription() + " + bacon";
    }
    int cost() const override {
        return pizza->cost() + 20;
    }
};

// client code
int main() {
    std::unique_ptr<Pizza> pizza = std::make_unique<Pizza>();
    std::cout << pizza.getDescription() << ": " << pizza.cost() << std::endl;
    pizza = std::make_unique<CheeseDecorator>(std::move(pizza));
    std::cout << pizza.getDescription() << ": " << pizza.cost() << std::endl;
    pizza = std::make_unique<BaconDecorator>(std::move(pizza));
    std::cout << pizza.getDescription() << ": " << pizza.cost() << std::endl;
    pizza = std::make_unique<CheeseDecorator>(std::move(pizza));
    std::cout << pizza.getDescription() << ": " << pizza.cost() << std::endl;
    pizza = std::make_unique<BaconDecorator>(std::move(pizza));
    std::cout << pizza.getDescription() << ": " << pizza.cost() << std::endl;
    return 0;
}
```
```c++=
pizza: 100
pizza + cheese: 110
pizza + cheese + bacon: 130
pizza + cheese + bacon + cheese: 140
pizza + cheese + bacon + cheese + bacon: 160
```

## (5) 門面模式 Facade
- 為複雜系統提供一個簡單的介面，透過一個門面類別來隱藏系統的細節
- ![image](https://hackmd.io/_uploads/B1Wiocfk1x.png)
```c++=
class TV {
public:
    void on() {
        std::cout << "Turning on the TV" << std::endl;
    }
    void off() {
        std::cout << "Turning off the TV" << std::endl;
    }
};

class Amplifier {
public:
    void on() {
        std::cout << "Turning on the amplifier" << std::endl;
    }
    void off() {
        std::cout << "Turning off the amplifier" << std::endl;
    }
    void setVolume(int level) {
        std::cout << "Setting amplifier volume to " << level << std::endl;
    }
};

class DVDPlayer {
public:
    void on() {
        std::cout << "Turning on the DVD player" << std::endl;
    }
    void off() {
        std::cout << "Turning off the DVD player" << std::endl;
    }
    void play(const std::string& movie) {
        std::cout << "Playing movie: " << movie << std::endl;
    }
};

class HomeTheaterFacade {
public:
    HomeTheaterFacade(TV* tv, Amplifier* amp, DVDPlayer* dvd)
        : tv_(tv), amp_(amp), dvd_(dvd) {}

    void watchMovie(const std::string& movie) {
        std::cout << "Get ready to watch a movie..." << std::endl;
        tv_->on();
        amp_->on();
        amp_->setVolume(5);
        dvd_->on();
        dvd_->play(movie);
    }

    void endMovie() {
        std::cout << "Shutting down the home theater..." << std::endl;
        dvd_->off();
        amp_->off();
        tv_->off();
    }

private:
    TV* tv_;
    Amplifier* amp_;
    DVDPlayer* dvd_;
};

// client
int main() {
    TV tv;
    Amplifier amp;
    DVDPlayer dvd;

    HomeTheaterFacade homeTheater(&tv, &amp, &dvd);

    homeTheater.watchMovie("Inception");
    std::cout << std::endl;
    homeTheater.endMovie();

    return 0;
}
```
```c++=
Get ready to watch a movie...
Turning on the TV
Turning on the amplifier
Setting amplifier volume to 5
Turning on the DVD player
Playing movie: Inception

Shutting down the home theater...
Turning off the DVD player
Turning off the amplifier
Turning off the TV
```

## (6) 享元模式 Flyweight
- 享元（Flyweight）這個術語源自拳擊界，在拳擊比賽中，Flyweight 指的是一個較輕量級(57kg以下)的選手級別
- 目的在減少物件建立時佔用的記憶體空間
- `將可共享的部分抽出，放到 factory 或實體中進行管理共享記憶體，達到了減少記憶體佔用`
- ![image](https://hackmd.io/_uploads/HJzIDX4ykx.png)
```c++=
class Circle {
public:
    Circle(std::string color) : color(color) {}

    void draw(int x, int y, int radius) {
        std::cout << "Drawing " << color << " circle at (" << x << ", " << y << ") with radius " << radius << std::endl;
    }

private:
    std::string color;
};

class ShapeFactory {
public:
    Circle* getCircle(std::string color) {
        Circle* circle = circleMap[color];
        if (circle == nullptr) {
            circle = new Circle(color);
            circleMap[color] = circle;
            std::cout << "Creating circle of color: " << color << std::endl;
        }
        return circle;
    }

private:
    std::map<std::string, Circle*> circleMap;
};

int main() {
    ShapeFactory factory;
    Circle* redCircle = factory.getCircle("red");
    redCircle->draw(10, 20, 30);
    Circle* blueCircle = factory.getCircle("blue");
    blueCircle->draw(50, 60, 70);
    Circle* anotherRedCircle = factory.getCircle("red");
    anotherRedCircle->draw(30, 30, 30);
    return 0;
}
```
```c++=
Creating circle of color: red
Drawing red circle at (10, 20) with radius 30
Creating circle of color: blue
Drawing blue circle at (50, 60) with radius 70
Drawing red circle at (30, 30) with radius 30
```

## (7) 代理模式 Proxy
- 題外話：正向代理跟反向代理
    - 正向代理: 服務多用戶，傳向伺服器前預處理，伺服器不知道用戶端是誰
    - 反向代理: duplicate server，proxy 整合多伺服器，用戶端不知道伺服器
- ![image](https://hackmd.io/_uploads/SJk_-jzyye.png)
- 避免 client 端繞過 proxy，通常可以搭配 factory pattern 一起使用
- 功用: 做為 cache、延遲載入資源、設定存取權限
```c++=
// 定義 Subject 介面，RealSubject 和 Proxy 都會實現此介面
class Subject {
public:
    virtual void request() const = 0;
    virtual ~Subject() = default;
};

// RealSubject 實現了真正的功能
class RealSubject : public Subject {
public:
    void request() const override {
        std::cout << "RealSubject: Handling request." << std::endl;
    }
};

// Proxy 會控制對 RealSubject 的訪問
class Proxy : public Subject {
private:
    mutable std::unique_ptr<RealSubject> real_subject_; // 延遲加載
    bool check_access() const {
        std::cout << "Proxy: Checking access prior to firing a real request.\n";
        return true;
    }

    void log_access() const {
        std::cout << "Proxy: Logging the time of request.\n";
    }

public:
    void request() const override {
        if (check_access()) {
            if (!real_subject_) {
                // 延遲實例化 RealSubject
                real_subject_ = std::make_unique<RealSubject>();
            }
            real_subject_->request();
            log_access();
        }
    }
};

// client
int main() {
    std::cout << "Client: Executing with a real subject:\n";
    RealSubject real_subject;
    real_subject.request();

    std::cout << "\nClient: Executing with a proxy:\n";
    Proxy proxy;
    proxy.request();
    return 0;
}
```
```c++=
Client: Executing with a real subject:
RealSubject: Handling request.

Client: Executing with a proxy:
Proxy: Checking access prior to firing a real request.
RealSubject: Handling request.
Proxy: Logging the time of request.
```
