设计模式学习笔记:理解与 C++ 示例

一组经过验证的“组织代码的经验”:当对象之间的关系开始变复杂时,设计模式帮助我们降低耦合、隔离变化、复用行为。

一、创建型模式

创建型模式提供了一种在创建对象的同时隐藏创建逻辑的方式,而不是使用 new 运算符直接实例化对象。这使得程序在判断针对某个给定实例需要创建哪些对象时更加灵活。关注“对象怎么创建”。当直接 new 对象会让业务代码依赖具体类,或者创建流程比较复杂时,可以考虑创建型模式。

1. 单例模式 Singleton

单例模式保证一个类在程序中只有一个实例,并提供一个全局访问点。常见场景有日志器、配置中心、线程池管理器等。

单例的核心不是“全局变量”,而是“唯一资源”。如果某个对象代表全局唯一状态,就可以考虑单例。但它也容易隐藏依赖关系,所以不要滥用。

C++ 示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <iostream>

class Logger {
public:
static Logger& instance() {
static Logger logger;
return logger;
}

void log(const std::string& message) {
std::cout << "[log] " << message << std::endl;
}

private:
Logger() = default;
Logger(const Logger&) = delete;
Logger& operator=(const Logger&) = delete;
};

int main() {
Logger::instance().log("system started");
}

2. 简单工厂 Simple Factory

简单工厂不属于 GoF 23 种模式,但很常用。它把对象创建逻辑集中到一个工厂函数中,根据参数返回不同类型的对象。

简单工厂适合类型较少、变化不频繁的场景。它能把 new 从业务代码里拿走,但新增类型时通常要修改工厂函数。

C++ 示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#include <iostream>
#include <memory>

class Shape {
public:
virtual void draw() = 0;
virtual ~Shape() = default;
};

class Circle : public Shape {
public:
void draw() override { std::cout << "draw circle\n"; }
};

class Rectangle : public Shape {
public:
void draw() override { std::cout << "draw rectangle\n"; }
};

class ShapeFactory {
public:
static std::unique_ptr<Shape> create(const std::string& type) {
if (type == "circle") return std::make_unique<Circle>();
if (type == "rectangle") return std::make_unique<Rectangle>();
return nullptr;
}
};

int main() {
auto shape = ShapeFactory::create("circle");
shape->draw();
}

3. 工厂方法 Factory Method

工厂方法把对象创建延迟到子类中。父类定义创建接口,子类决定具体创建哪一种产品。

简单工厂是“一个工厂判断类型”,工厂方法是“不同工厂负责不同产品”。当新增产品时,可以新增工厂类,减少对原有工厂的修改。

C++ 示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
#include <iostream>
#include <memory>

class Button {
public:
virtual void render() = 0;
virtual ~Button() = default;
};

class WindowsButton : public Button {
public:
void render() override { std::cout << "Windows button\n"; }
};

class MacButton : public Button {
public:
void render() override { std::cout << "Mac button\n"; }
};

class Dialog {
public:
virtual std::unique_ptr<Button> createButton() = 0;
virtual ~Dialog() = default;
};

class WindowsDialog : public Dialog {
public:
std::unique_ptr<Button> createButton() override {
return std::make_unique<WindowsButton>();
}
};

class MacDialog : public Dialog {
public:
std::unique_ptr<Button> createButton() override {
return std::make_unique<MacButton>();
}
};

int main() {
std::unique_ptr<Dialog> dialog = std::make_unique<WindowsDialog>();
auto button = dialog->createButton();
button->render();
}
  1. 抽象工厂 Abstract Factory

抽象工厂用于创建一组相关对象。它提供多个创建接口,每个具体工厂负责创建同一产品族中的对象。

工厂方法关注“创建一个产品”,抽象工厂关注“创建一整套产品”。比如 Windows 风格的按钮和输入框应该成套出现,Mac 风格也应该成套出现。

C++ 示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
#include <iostream>
#include <memory>

class Button {
public:
virtual void paint() = 0;
virtual ~Button() = default;
};

class Checkbox {
public:
virtual void paint() = 0;
virtual ~Checkbox() = default;
};

class WindowsButton : public Button {
public:
void paint() override { std::cout << "Windows button\n"; }
};

class WindowsCheckbox : public Checkbox {
public:
void paint() override { std::cout << "Windows checkbox\n"; }
};

class UIFactory {
public:
virtual std::unique_ptr<Button> createButton() = 0;
virtual std::unique_ptr<Checkbox> createCheckbox() = 0;
virtual ~UIFactory() = default;
};

class WindowsFactory : public UIFactory {
public:
std::unique_ptr<Button> createButton() override {
return std::make_unique<WindowsButton>();
}

std::unique_ptr<Checkbox> createCheckbox() override {
return std::make_unique<WindowsCheckbox>();
}
};

int main() {
std::unique_ptr<UIFactory> factory = std::make_unique<WindowsFactory>();
factory->createButton()->paint();
factory->createCheckbox()->paint();
}

5. 建造者模式 Builder

建造者模式把复杂对象的构建过程拆成多个步骤,让同样的构建过程可以创建不同表示。

当构造函数参数太多,或者对象创建需要按步骤设置时,Builder 会比一个巨大构造函数更清晰。它尤其适合配置类、SQL 构造器、HTTP 请求构造器。

C++ 示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
#include <iostream>
#include <string>

class Computer {
public:
std::string cpu;
std::string memory;
std::string disk;

void show() const {
std::cout << cpu << ", " << memory << ", " << disk << std::endl;
}
};

class ComputerBuilder {
public:
ComputerBuilder& setCpu(const std::string& cpu) {
computer.cpu = cpu;
return *this;
}

ComputerBuilder& setMemory(const std::string& memory) {
computer.memory = memory;
return *this;
}

ComputerBuilder& setDisk(const std::string& disk) {
computer.disk = disk;
return *this;
}

Computer build() {
return computer;
}

private:
Computer computer;
};

int main() {
Computer pc = ComputerBuilder()
.setCpu("i7")
.setMemory("16GB")
.setDisk("1TB SSD")
.build();

pc.show();
}

6. 原型模式 Prototype

原型模式通过复制已有对象来创建新对象,而不是从头构造。

如果对象创建成本高,或者运行时才知道具体类型,原型模式很有用。C++ 中通常用虚函数 clone() 实现多态复制。

C++ 示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <iostream>
#include <memory>

class Enemy {
public:
virtual std::unique_ptr<Enemy> clone() const = 0;
virtual void attack() const = 0;
virtual ~Enemy() = default;
};

class RobotEnemy : public Enemy {
public:
std::unique_ptr<Enemy> clone() const override {
return std::make_unique<RobotEnemy>(*this);
}

void attack() const override {
std::cout << "Robot enemy attacks\n";
}
};

int main() {
std::unique_ptr<Enemy> prototype = std::make_unique<RobotEnemy>();
auto enemy = prototype->clone();
enemy->attack();
}

二、结构型模式

结构型模式关注对象之间的组合和关系,旨在解决如何构建灵活且可复用的类和对象结构。

7. 适配器模式 Adapter

适配器模式把一个类的接口转换成客户端期望的另一个接口。

适配器像一个转换插头。老代码、第三方库、历史接口不方便改时,可以写一个适配器,让新代码按统一接口使用它。

C++ 示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#include <iostream>

class OldPrinter {
public:
void oldPrint() {
std::cout << "print by old printer\n";
}
};

class Printer {
public:
virtual void print() = 0;
virtual ~Printer() = default;
};

class PrinterAdapter : public Printer {
public:
explicit PrinterAdapter(OldPrinter& oldPrinter) : oldPrinter(oldPrinter) {}

void print() override {
oldPrinter.oldPrint();
}

private:
OldPrinter& oldPrinter;
};

int main() {
OldPrinter oldPrinter;
PrinterAdapter adapter(oldPrinter);
adapter.print();
}

8. 桥接模式 Bridge

桥接模式把抽象部分和实现部分分离,让它们可以独立变化。

当一个类有两个变化维度时,不要用继承堆出一大堆组合类。桥接模式把其中一个维度抽出去,用组合代替继承。

C++ 示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
#include <iostream>
#include <memory>

class Renderer {
public:
virtual void renderCircle() = 0;
virtual ~Renderer() = default;
};

class VectorRenderer : public Renderer {
public:
void renderCircle() override { std::cout << "draw circle by vector\n"; }
};

class Shape {
public:
explicit Shape(std::unique_ptr<Renderer> renderer)
: renderer(std::move(renderer)) {}

virtual void draw() = 0;
virtual ~Shape() = default;

protected:
std::unique_ptr<Renderer> renderer;
};

class Circle : public Shape {
public:
using Shape::Shape;

void draw() override {
renderer->renderCircle();
}
};

int main() {
Circle circle(std::make_unique<VectorRenderer>());
circle.draw();
}

9. 组合模式 Composite

组合模式把对象组织成树形结构,使客户端可以用一致方式处理单个对象和对象集合。

组合模式适合“部分-整体”结构,比如文件系统、组织架构、UI 组件树。关键是让叶子节点和容器节点拥有同一个接口。

C++ 示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
#include <iostream>
#include <memory>
#include <vector>

class FileSystemNode {
public:
virtual void show() = 0;
virtual ~FileSystemNode() = default;
};

class File : public FileSystemNode {
public:
explicit File(std::string name) : name(std::move(name)) {}

void show() override {
std::cout << "file: " << name << std::endl;
}

private:
std::string name;
};

class Folder : public FileSystemNode {
public:
void add(std::unique_ptr<FileSystemNode> node) {
children.push_back(std::move(node));
}

void show() override {
for (auto& child : children) {
child->show();
}
}

private:
std::vector<std::unique_ptr<FileSystemNode>> children;
};

int main() {
Folder folder;
folder.add(std::make_unique<File>("main.cpp"));
folder.add(std::make_unique<File>("README.md"));
folder.show();
}

10. 装饰器模式 Decorator

装饰器模式在不修改原类的情况下,动态地给对象增加功能。

装饰器本质是“套娃式增强”。它和继承都能扩展功能,但装饰器是在运行时组合行为,灵活性更高。

C++ 示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#include <iostream>
#include <memory>

class Coffee {
public:
virtual double cost() const = 0;
virtual ~Coffee() = default;
};

class SimpleCoffee : public Coffee {
public:
double cost() const override { return 10.0; }
};

class MilkDecorator : public Coffee {
public:
explicit MilkDecorator(std::unique_ptr<Coffee> coffee)
: coffee(std::move(coffee)) {}

double cost() const override {
return coffee->cost() + 2.0;
}

private:
std::unique_ptr<Coffee> coffee;
};

int main() {
std::unique_ptr<Coffee> coffee =
std::make_unique<MilkDecorator>(std::make_unique<SimpleCoffee>());

std::cout << coffee->cost() << std::endl;
}

11. 外观模式 Facade

外观模式为复杂子系统提供一个简单统一的入口。

外观不是为了隐藏所有细节,而是给常用流程提供一个更友好的 API。它能让调用方少知道一些内部模块的组合方式。

C++ 示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#include <iostream>

class CPU {
public:
void start() { std::cout << "CPU start\n"; }
};

class Memory {
public:
void load() { std::cout << "Memory load\n"; }
};

class ComputerFacade {
public:
void start() {
cpu.start();
memory.load();
std::cout << "Computer ready\n";
}

private:
CPU cpu;
Memory memory;
};

int main() {
ComputerFacade computer;
computer.start();
}

12. 享元模式 Flyweight

享元模式通过共享对象来减少内存开销,适合大量对象拥有相同内部状态的场景。

享元的重点是区分内部状态和外部状态。可以共享的放到享元对象里,不可共享的由调用方传入。

C++ 示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
#include <iostream>
#include <memory>
#include <string>
#include <unordered_map>

class TreeType {
public:
TreeType(std::string name, std::string color)
: name(std::move(name)), color(std::move(color)) {}

void draw(int x, int y) const {
std::cout << name << " " << color << " at " << x << ", " << y << std::endl;
}

private:
std::string name;
std::string color;
};

class TreeFactory {
public:
std::shared_ptr<TreeType> getTreeType(const std::string& name, const std::string& color) {
std::string key = name + ":" + color;
if (!types.count(key)) {
types[key] = std::make_shared<TreeType>(name, color);
}
return types[key];
}

private:
std::unordered_map<std::string, std::shared_ptr<TreeType>> types;
};

int main() {
TreeFactory factory;
auto type = factory.getTreeType("pine", "green");
type->draw(10, 20);
type->draw(30, 40);
}

13. 代理模式 Proxy

代理模式为真实对象提供一个替身,用于控制访问、延迟加载、权限检查或缓存。

代理和装饰器都包了一层对象,但目的不同:装饰器偏向增强功能,代理偏向控制访问。

C++ 示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
#include <iostream>
#include <memory>

class Image {
public:
virtual void display() = 0;
virtual ~Image() = default;
};

class RealImage : public Image {
public:
explicit RealImage(std::string filename) : filename(std::move(filename)) {
std::cout << "load " << this->filename << std::endl;
}

void display() override {
std::cout << "display " << filename << std::endl;
}

private:
std::string filename;
};

class ImageProxy : public Image {
public:
explicit ImageProxy(std::string filename) : filename(std::move(filename)) {}

void display() override {
if (!image) {
image = std::make_unique<RealImage>(filename);
}
image->display();
}

private:
std::string filename;
std::unique_ptr<RealImage> image;
};

int main() {
ImageProxy image("photo.png");
image.display();
image.display();
}

三、行为型模式

行为型模式关注对象之间的通信和交互,旨在解决对象之间的责任分配和算法的封装。