# Blog 4|設計模式 - 結構型模式
- Reference :
- https://shengyu7697.github.io/archives/
- https://www.jyt0532.com/
- https://refactoringguru.cn/design-patterns/catalog
## (1) 適配器模式 Adapter
- 
- 想要重用某個已經存在的類,但其接口與目前系統不匹配的情況下。`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):定義具體的行為,並可以有不同的具體實現類別。
- 
```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:表示複雜元件,包含其他元件(可以是葉節點或其他複雜元件)。
- 讓樹狀結構中的物件與物件集合能被同樣對待
- 
```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)
- 
```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
- 為複雜系統提供一個簡單的介面,透過一個門面類別來隱藏系統的細節
- 
```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, &, &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 或實體中進行管理共享記憶體,達到了減少記憶體佔用`
- 
```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 整合多伺服器,用戶端不知道伺服器
- 
- 避免 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.
```