Observer (观察者)

观察者模式是一种行为设计模式,在对象之间建立一种观察关系,使得在被观察对象的某个事件发生时,自动通知多个正在观察该对象的其他对象。

观察者模式的实现过程如下:

  • 第一步,定义观察者基类Observer,它对外预留了update接口,代表更新观察者状态,被观察者将会通过调用这个接口来通知观察者;
  • 第二步,定义被观察者基类Subject,它对外预留了几个接口:
    • registerObserver:注册新的观察者,与之建立观察关系
    • removeObserver:移除指定的观察者,与之解出观察关系
    • notifyObservers:通知所有正在观察当前对象的观察者
  • 第三步,定义具体的被观察者类(WeatherStationStockMarket等),它们实际上在内部都需要使用一个观察者指针数组来记录所有注册的观察者,并且实现预留的三个接口,其中notifyObservers需要通知当前数组中的所有观察者。如果被观察者的状态被其它方法修改,需要主动触发notifyObservers来发出通知。
  • 第四步,定义具体的观察者类(DisplayDeviceStockDisplay等),需要负责实现update接口的具体行为,例如获取传递过来的参数并记录到日志中。

示例代码如下(为了让不同类型的被观察者可以使用不同参数来通知观察者,这里使用了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
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
#include <algorithm>
#include <iostream>
#include <memory>
#include <string>
#include <utility>
#include <vector>

// 抽象观察者基类
template <typename... Args>
class Observer {
public:
virtual void update(Args... args) = 0;
virtual ~Observer() = default;
};

// 抽象被观察者基类
template <typename... Args>
class Subject {
public:
virtual void
registerObserver(std::shared_ptr<Observer<Args...>> observer) = 0;
virtual void
removeObserver(std::shared_ptr<Observer<Args...>> observer) = 0;
virtual void notifyObservers(Args... args) = 0;
virtual ~Subject() = default;
};

// 具体被观察者类 - WeatherStation
class WeatherStation : public Subject<double, double, double> {
public:
void registerObserver(
std::shared_ptr<Observer<double, double, double>> observer) override {
m_observers.push_back(observer);
}

void removeObserver(
std::shared_ptr<Observer<double, double, double>> observer) override {
m_observers.erase(
std::remove(m_observers.begin(), m_observers.end(), observer),
m_observers.end());
}

void notifyObservers(double temperature, double humidity,
double pressure) override {
for (auto &observer : m_observers) {
observer->update(temperature, humidity, pressure);
}
}

void setMeasurements(double temp, double hum, double pres) {
notifyObservers(temp, hum, pres);
}

private:
std::vector<std::shared_ptr<Observer<double, double, double>>> m_observers;
};

// 具体观察者类 - DisplayDevice
class DisplayDevice : public Observer<double, double, double> {
public:
explicit DisplayDevice(std::string name) : m_deviceName(std::move(name)) {}

void update(double temperature, double humidity, double pressure) override {
std::cout << "Device " << m_deviceName
<< " - Temperature: " << temperature
<< "°C, Humidity: " << humidity << "%, Pressure: " << pressure
<< "hPa\n";
}

private:
std::string m_deviceName;
};

// 具体被观察者类 - StockMarket
class StockMarket : public Subject<std::string, double> {
public:
void registerObserver(
std::shared_ptr<Observer<std::string, double>> observer) override {
m_observers.push_back(observer);
}

void removeObserver(
std::shared_ptr<Observer<std::string, double>> observer) override {
m_observers.erase(
std::remove(m_observers.begin(), m_observers.end(), observer),
m_observers.end());
}

void notifyObservers(std::string stock, double price) override {
for (auto &observer : m_observers) { observer->update(stock, price); }
}

void setStockPrice(std::string stock, double price) {
notifyObservers(stock, price);
}

private:
std::vector<std::shared_ptr<Observer<std::string, double>>> m_observers;
};

// 具体观察者类 - StockDisplay
class StockDisplay : public Observer<std::string, double> {
public:
explicit StockDisplay(std::string name) : m_displayName(std::move(name)) {}

void update(std::string stock, double price) override {
std::cout << "StockDisplay " << m_displayName << " - Stock: " << stock
<< ", Price: " << price << "\n";
}

private:
std::string m_displayName;
};

// 主函数
int main() {
auto weatherStation = std::make_shared<WeatherStation>();

auto display1 = std::make_shared<DisplayDevice>("WeatherDisplay1");
auto display2 = std::make_shared<DisplayDevice>("WeatherDisplay2");

weatherStation->registerObserver(display1);
weatherStation->registerObserver(display2);

std::cout << "Weather Station Day 1:\n";
weatherStation->setMeasurements(25.0, 65.0, 1013.0);
std::cout << "Weather Station Day 2:\n";
weatherStation->setMeasurements(30.0, 70.0, 1012.0);
weatherStation->removeObserver(display1); // remove display1
std::cout << "Weather Station Day 3:\n";
weatherStation->setMeasurements(28.0, 60.0, 1011.0);

auto stockMarket = std::make_shared<StockMarket>();

auto stockDisplay1 = std::make_shared<StockDisplay>("StockDisplay1");
auto stockDisplay2 = std::make_shared<StockDisplay>("StockDisplay2");

stockMarket->registerObserver(stockDisplay1);
stockMarket->registerObserver(stockDisplay2);

std::cout << "Stock Market Update 1:\n";
stockMarket->setStockPrice("AAPL", 150.0);
std::cout << "Stock Market Update 2:\n";
stockMarket->setStockPrice("GOOGL", 2750.0);
stockMarket->removeObserver(stockDisplay1); // remove stockDisplay1
std::cout << "Stock Market Update 3:\n";
stockMarket->setStockPrice("AMZN", 3400.0);

return 0;
}

运行结果如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Weather Station Day 1:
Device WeatherDisplay1 - Temperature: 25°C, Humidity: 65%, Pressure: 1013hPa
Device WeatherDisplay2 - Temperature: 25°C, Humidity: 65%, Pressure: 1013hPa
Weather Station Day 2:
Device WeatherDisplay1 - Temperature: 30°C, Humidity: 70%, Pressure: 1012hPa
Device WeatherDisplay2 - Temperature: 30°C, Humidity: 70%, Pressure: 1012hPa
Weather Station Day 3:
Device WeatherDisplay2 - Temperature: 28°C, Humidity: 60%, Pressure: 1011hPa
Stock Market Update 1:
StockDisplay StockDisplay1 - Stock: AAPL, Price: 150
StockDisplay StockDisplay2 - Stock: AAPL, Price: 150
Stock Market Update 2:
StockDisplay StockDisplay1 - Stock: GOOGL, Price: 2750
StockDisplay StockDisplay2 - Stock: GOOGL, Price: 2750
Stock Market Update 3:
StockDisplay StockDisplay2 - Stock: AMZN, Price: 3400

除了观察者模式,还有一个与之相似的订阅发布模式,这个模式并不在二十几个经典设计模式之中, 但是两者经常会被放在一起讨论,对订阅发布模式的学习见后续笔记。

State (状态)

状态模式是一种行为设计模式,让我们能在一个对象的内部状态变化时改变其行为,使其看上去就像改变了自身所属的类一样。

首先定义状态类基类State,然后继承得到具体的开状态OnState和闭状态OffState,状态类提供了handle接口。 接下来,定义持有状态的上下文类Light,它的构造需要提供一个状态作为初始状态, 提供的set_state方法用于修改或查看当前的内部状态,提供的show方法的调用效果由当前的内部状态决定。

示例代码如下

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
49
50
51
#include <iostream>
#include <memory>

// 状态类基类
class State {
public:
virtual void handle() = 0;
virtual ~State() = default;
};

// 具体状态类 - 开状态
class OnState : public State {
public:
void handle() override { std::cout << "The light is now ON.\n"; }
};

// 具体状态类 - 关状态
class OffState : public State {
public:
void handle() override { std::cout << "The light is now OFF.\n"; }
};

// 上下文类
class Light {
public:
explicit Light(std::unique_ptr<State> initialState)
: m_state(std::move(initialState)) {}

void set_state(std::unique_ptr<State> newState) {
m_state = std::move(newState);
}

void handle() { m_state->handle(); }

private:
std::unique_ptr<State> m_state;
};

int main() {
auto light = std::make_unique<Light>(std::make_unique<OffState>());

light->handle();

light->set_state(std::make_unique<OnState>());
light->handle();

light->set_state(std::make_unique<OffState>());
light->handle();

return 0;
}

运行结果如下

1
2
3
The light is now OFF.
The light is now ON.
The light is now OFF.

状态模式与有限状态机的概念紧密相关,两者的侧重点有所不同:

  • 状态模式:调用方法的效果由内部状态决定,通常对外提供修改和查询内部状态的方法;
  • 有限状态机:状态之间的转移关系构成一个单向图,对外提供的方法通常需要输入一个状态,然后结合现有的内部状态,查询转移关系,得到新的内部状态。

Strategy (策略)

策略模式是一种行为设计模式,允许用户定义一系列算法,并将每种算法分别放入独立的类中,使算法的对象能够相互替换。

策略模式是非常简单显然的做法,只需要给上下文对象提供一个可执行的策略对象即可,在C/C++中使用回调函数其实都可以算作策略模式,对于Java这种没有函数只有对象的语言,才有必要专门定义策略类和对象。

首先我们需要定义策略基类StrategyBase,它预留了执行接口execute,然后派生出多个具体的策略类(StrategyAStrategyB等)。 上下文类Context需要根据不同的策略执行performTask方法,它可以被赋予一个策略类指针,如果在调用performTask方法时持有策略类指针,就调用对应的执行方法execute,否则报错。

示例代码如下

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
49
50
51
52
#include <iostream>
#include <memory>

// 策略基类
class StrategyBase {
public:
virtual ~StrategyBase() = default;
virtual void execute() const = 0;
};

// 具体策略A
class StrategyA : public StrategyBase {
public:
void execute() const override {
std::cout << "execute: Strategy A" << std::endl;
}
};

// 具体策略B
class StrategyB : public StrategyBase {
public:
void execute() const override {
std::cout << "execute: Strategy B" << std::endl;
}
};

// 上下文类
class Context {
private:
std::unique_ptr<StrategyBase> m_strategy;

public:
Context &setStrategy(std::unique_ptr<StrategyBase> newStrategy) {
m_strategy = std::move(newStrategy);
return *this;
}

void performTask() const {
if (m_strategy) { m_strategy->execute(); }
else { std::cout << "No Strategy" << std::endl; }
}
};

int main() {
Context context;

context.setStrategy(std::make_unique<StrategyA>()).performTask();

context.setStrategy(std::make_unique<StrategyB>()).performTask();

return 0;
}

Template Method (模板方法)

模板方法模式是一种行为设计模式,它在基类中定义了一个算法的框架,允许子类在不修改结构的情况下重写算法的特定步骤。

模板方法的实现很简单:抽象基类AbstractClass定义了算法框架(模板),对应的方法名称是templateMethod,它会依次调用很多子方法来完成算法的对应步骤:

  • 有的子方法是虚函数,基类已经提供了默认实现,子类可以选择继承或重写
  • 有的子方法是纯虚函数,子类必须负责具体实现

具体的派生类(ConcreteClassAConcreteClassB等)需要负责实现那些必要的步骤,可以选择重写那些已经有默认实现的步骤, 派生类仍然通过templateMethod来提供完整算法的使用。

示例代码如下

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
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
#include <iostream>
#include <memory>
#include <string>

// 抽象类
class AbstractClass {
public:
virtual ~AbstractClass() = default;

// 模板方法,依次调用很多子方法来完成整个功能
void templateMethod() const {
std::string data = initialize_data();
data = base_operation_1(data);
data = required_operation_1(data);
data = base_operation_2(data);
data = required_operation_2(data);
data = hook(data);
finalize(data);
}

protected:
// 初始化数据的方法,子类可以覆盖它
virtual std::string initialize_data() const { return "Initial Data"; }

// 有默认的实现,子类可以覆盖它
virtual std::string base_operation_1(const std::string &data) const {
return data + " \n-> AbstractClass: base_operation_1";
}

// 有默认的实现,子类可以覆盖它
virtual std::string base_operation_2(const std::string &data) const {
return data + " \n-> AbstractClass: base_operation_2";
}

// 必须由子类负责实现
virtual std::string required_operation_1(const std::string &data) const = 0;
// 必须由子类负责实现
virtual std::string required_operation_2(const std::string &data) const = 0;

// 有默认的实现,子类可以覆盖它
virtual std::string hook(const std::string &data) const {
return data + " \n-> AbstractClass: hook";
}

// 有默认的实现,子类可以覆盖它
virtual void finalize(const std::string &data) const {
std::cout << "Final result: " << data << std::endl;
}
};

// 具体类A
class ConcreteClassA : public AbstractClass {
protected:
std::string required_operation_1(const std::string &data) const override {
return data + " \n-> ConcreteClassA: required_operation_1";
}

std::string required_operation_2(const std::string &data) const override {
return data + " \n-> ConcreteClassA: required_operation_2";
}

std::string hook(const std::string &data) const override {
return data + " \n-> ConcreteClassA: hook";
}
};

// 具体类B
class ConcreteClassB : public AbstractClass {
protected:
std::string required_operation_1(const std::string &data) const override {
return data + " \n-> ConcreteClassB: required_operation_1";
}

std::string required_operation_2(const std::string &data) const override {
return data + " \n-> ConcreteClassB: required_operation_2";
}
};

int main() {
std::cout << "Using ConcreteClassA:\n";
auto concreteClassA = std::make_unique<ConcreteClassA>();
concreteClassA->templateMethod();

std::cout << "Using ConcreteClassB:\n";
auto concreteClassB = std::make_unique<ConcreteClassB>();
concreteClassB->templateMethod();

return 0;
}

运行结果如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Using ConcreteClassA:
Final result: Initial Data
-> AbstractClass: base_operation_1
-> ConcreteClassA: required_operation_1
-> AbstractClass: base_operation_2
-> ConcreteClassA: required_operation_2
-> ConcreteClassA: hook
Using ConcreteClassB:
Final result: Initial Data
-> AbstractClass: base_operation_1
-> ConcreteClassB: required_operation_1
-> AbstractClass: base_operation_2
-> ConcreteClassB: required_operation_2
-> AbstractClass: hook

Visitor (访问者)

访问者模式是一种行为设计模式,它能将算法与其所作用的对象隔离开来,使我们可以在不改变各元素类的前提下定义作用于这些元素类的新操作。

我们的情景是:考虑一个图形元素基类Shape,以及扩展类CircleRectangle,它们有各自不同的属性和方法。 现在希望在尽量少改动元素的前提下,定义很多对不同元素的访问操作,例如获取id,获取详细信息等, 达到下面的效果(这里shapes是包括不同的图形元素的集合)

1
2
3
for (auto &shape : shapes) {
// visit the shape to show its id
}

为了实现上述效果,C++需要进行类型前置声明等合规性处理,因此代码看起来非常混乱:

  • 第一步,我们需要对所有的派生自Shape的具体图形元素类型进行前置声明;

  • 第二步,定义访问者基类ShapeVisitor,它包括对每一个具体元素预留名为visit的访问接口,参数是对应元素指针;

  • 第三步,定义图形元素基类,它预留了一个名为accept的接受访问接口,参数是访问者基类指针;

  • 第四步,定义所有的具体图形元素(CircleRectangle等),它们都继承自Shape,需要实现接受访问者基类ShapeVisitor访问的接口,内容均为

    1
    2
    3
    void accept(ShapeVisitor *visitor) override {
    visitor->visit(this);
    }

  • 第五步,定义具体的访问者(ShapeIdVisitorShapeDetailVisitor等),它们都继承自ShapeVisitor,需要负责实现对每一个具体元素类型的visit行为。

通过上述代码,我们将遍历集合进行的访问反转,实际实现的是下面的效果

1
2
3
for (auto &shape : shapes) {
shape->accept(&idVisitor);
}

示例代码如下

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
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
#include <iostream>
#include <memory>
#include <vector>

// 前向声明
class Circle;
class Rectangle;

// 访问者接口
class ShapeVisitor {
public:
virtual void visit(Circle *shape) = 0;
virtual void visit(Rectangle *shape) = 0;
virtual ~ShapeVisitor() = default;
};

// 图形元素接口
class Shape {
public:
virtual void accept(ShapeVisitor *visitor) = 0;
virtual int getId() const = 0;
virtual ~Shape() = default;
};

// 圆形类
class Circle : public Shape {
public:
Circle(int id, double radius) : m_id(id), m_radius(radius) {}

void accept(ShapeVisitor *visitor) override { visitor->visit(this); }

int getId() const override { return m_id; }

double getRadius() const { return m_radius; }

private:
int m_id;
double m_radius;
};

// 矩形类
class Rectangle : public Shape {
public:
Rectangle(int id, double width, double height)
: m_id(id), m_width(width), m_height(height) {}

void accept(ShapeVisitor *visitor) override { visitor->visit(this); }

int getId() const override { return m_id; }

double getWidth() const { return m_width; }

double getHeight() const { return m_height; }

private:
int m_id;
double m_width;
double m_height;
};

// 获取图形ID的访问者类
class ShapeIdVisitor : public ShapeVisitor {
public:
void visit(Circle *shape) override {
std::cout << "Circle ID: " << shape->getId() << "\n";
}

void visit(Rectangle *shape) override {
std::cout << "Rectangle ID: " << shape->getId() << "\n";
}
};

// 获取图形详细信息的访问者类
class ShapeDetailVisitor : public ShapeVisitor {
public:
void visit(Circle *shape) override {
std::cout << "Circle ID: " << shape->getId()
<< ", Radius: " << shape->getRadius() << "\n";
}

void visit(Rectangle *shape) override {
std::cout << "Rectangle ID: " << shape->getId()
<< ", Width: " << shape->getWidth()
<< ", Height: " << shape->getHeight() << "\n";
}
};

int main() {
std::vector<std::unique_ptr<Shape>> shapes;
shapes.push_back(std::make_unique<Circle>(1, 10.0));
shapes.push_back(std::make_unique<Rectangle>(2, 20.0, 15.0));

ShapeIdVisitor idVisitor;
ShapeDetailVisitor detailVisitor;

std::cout << "Using ShapeIdVisitor:\n";
for (auto &shape : shapes) { shape->accept(&idVisitor); }

std::cout << "Using ShapeDetailVisitor:\n";
for (auto &shape : shapes) { shape->accept(&detailVisitor); }

return 0;
}

运行结果如下

1
2
3
4
5
6
Using ShapeIdVisitor:
Circle ID: 1
Rectangle ID: 2
Using ShapeDetailVisitor:
Circle ID: 1, Radius: 10
Rectangle ID: 2, Width: 20, Height: 15

需要注意的是:访问者模式其实是违背开闭原则的!虽然添加新的访问者类型是不需要修改其它代码的, 但是添加新的图形类是需要修改现有代码的,对于C++,我们还需要维护前置声明等很多语法细节。