大纲
观察者模式
观察者模式的概念
观察者模式(Observer Pattern)是一种行为型设计模式。用于建立对象之间的一种一对多依赖关系,使得每当一个对象状态发生改变时,其相关依赖对象皆得到通知并被自动更新。观察者模式的别名包括发布 - 订阅模式(Publish/Subscribe)、模型 - 视图模式(Model/View)、源 - 监听器模式(Source/Listener),或从属者模式(Dependents)。这种模式常用于事件处理、数据绑定和回调函数等场景。
观察者模式的结构组成
- Subject(主题 / 被观察者)
- 定义 Subject 接口,负责维护观察者列表(注册、移除、通知)
- 在状态发生变化时,通知所有观察者
- Observer(观察者)
- 定义更新接口,负责接收通知
- 依赖于 Subject,当 Subject 的状态发生变化时作出反应
- ConcreteSubject(具体主题)
- 实现了 Subject 接口的具体类,包含状态数据
- 状态改变时调用
notify()
方法通知所有观察者
- ConcreteObserver(具体观察者)
- 实现 Observer 接口的具体类
- 当 Subject 的状态发生改变时,更新自身状态
观察者模式的工作流程
- 观察者注册到主题对象上(通常通过
attach()
函数) - 当主题对象的状态发生改变时,调用
notify()
函数 - 每个注册的观察者收到通知并执行相应的
update()
函数
观察者模式的优缺点
- 优点:
- 解耦:观察者和被观察者之间是松耦合的
- 可扩展性强:可以动态添加或者移除观察者
- 广播通信机制:适合一个事件影响多个对象的场景
- 缺点:
- 如果观察者数量很多,通知过程可能比较耗时
- 如果观察者和主题之间的依赖链过长,可能会引发性能或调试问题(例如循环更新)
观察者模式的使用场景
- GUI 软件中,实现按钮点击通知监听器
- 发布 / 订阅系统(如新闻、消息推送)
- 数据模型变化后通知视图更新(MVC 模式)
观察者模式的使用
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
| #include <iostream> #include <unordered_map> #include <list>
using namespace std;
class Observer {
public: virtual void update(int state) = 0;
virtual ~Observer() {}
};
class ConcreteObserverA : public Observer {
public: void update(int state) override { cout << "ConcreteObserverA receive message, state is " << state << endl; }
};
class ConcreteObserverB : public Observer {
public: void update(int state) override { cout << "ConcreteObserverB receive message, state is " << state << endl; }
};
class ConcreteObserverC : public Observer {
public: void update(int state) override { cout << "ConcreteObserverC receive message, state is " << state << endl; }
};
class Subject { public: virtual void attach(int state, Observer *observer) = 0;
virtual void detach(int state, Observer *observer) = 0;
virtual void notify(int state) = 0;
virtual ~Subject() {} };
class ConcreteSubject : public Subject {
public: void attach(int state, Observer *observer) { auto it = _subMap.find(state); if (it != _subMap.end()) { it->second.push_back(observer); } else { list<Observer *> list; list.push_back(observer); _subMap.insert(make_pair(state, list)); } }
void detach(int state, Observer *observer) { auto it = _subMap.find(state); if (it != _subMap.end()) { list<Observer *> &list = it->second; for (auto it = list.begin(); it != list.end();) { if (*it == observer) { it = list.erase(it); } else { ++it; } } } }
void notify(int state) { auto it = _subMap.find(state); if (it != _subMap.end()) { for (Observer *pObserver : it->second) { pObserver->update(state); } } }
private: unordered_map<int, list<Observer *>> _subMap;
};
int main() { ConcreteSubject subject;
ConcreteObserverA obsA; ConcreteObserverB obsB; ConcreteObserverC obsC;
subject.attach(1, &obsA); subject.attach(1, &obsB); subject.attach(2, &obsA); subject.attach(2, &obsC);
subject.detach(2, &obsA);
subject.notify(1); subject.notify(2);
return 0; }
|
程序运行的输出结果如下:
1 2 3
| ConcreteObserverA receive message, state is 1 ConcreteObserverB receive message, state is 1 ConcreteObserverC receive message, state is 2
|
适配器模式
适配器模式的概念
适配器模式(Adapter Pattern)是一种结构型设计模式,主要用于将一个类的接口转换成客户端所期望的另一个接口,使原本接口不兼容的类可以一起工作。它就像 “插头转换器” 一样,可以帮助两个无法直接连接的系统建立连接。简而言之,适配器模式可以让已有的类在不修改源码的情况下,能和其他类协同工作,解决 “接口不兼容” 的问题。
适配器模式的结构组成
- 目标接口(Target):客户端期望的接口。
- 待适配的类(Adaptee):已有的接口,功能已经实现,但接口不符合要求。
- 适配器(Adapter):将 Adaptee 的接口转换为 Target 的接口。
适配器模式的优缺点
- 优点:
- 提高代码复用性。
- 解耦系统与外部接口。
- 避免修改已有代码(符合 “开闭原则”)。
- 缺点:
- 增加代码复杂性。
- 过多使用适配器,可能会让系统变得难以理解和维护。
适配器模式的使用场景
- 系统需要使用一些现有的类,但这些类的接口与系统期望的不一致。
- 想复用一些功能强大的旧代码,但不希望修改它们。
适配器模式的实现
适配器模式的常见实现方式有以下两种:
- (1) 类适配器(使用继承实现),适配器继承自 Adaptee,并实现 Target 接口。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| class Target { public: virtual void request() = 0; };
class Adaptee { public: void specificRequest() { cout << "Adaptee specific request" << endl; } };
class Adapter : public Target, private Adaptee { public: void request() override { specificRequest(); } };
|
- (2) 对象适配器(使用组合实现),适配器持有一个 Adaptee 对象的引用,并实现 Target 接口。
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
| class Target {
public: virtual void request() = 0;
};
class Adaptee {
public: void specificRequest() { cout << "Adaptee specific request" << endl; }
};
class Adapter : public Target {
private: Adaptee* adaptee;
public: Adapter(Adaptee* a) : adaptee(a) {
}
void request() override { adaptee->specificRequest(); }
};
|
适配器模式的使用
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
| #include <iostream>
using namespace std;
class VGA {
public: virtual void play() = 0;
virtual ~VGA() {}
};
class Device01 : public VGA {
public: void play() override { cout << "使用 VGA 接口的投影仪播放视频" << endl; }
};
class Computer {
public: void playVideo(VGA *vga) { vga->play(); }
};
class HDMI {
public: virtual void play() = 0;
virtual ~HDMI() {}
};
class Device02 : public HDMI {
public: void play() override { cout << "使用 HDMI 接口的投影仪播放视频" << endl; }
};
class VGAToHDMIAdapter : public VGA {
public: VGAToHDMIAdapter(HDMI *p) : pHdmi(p) {
}
void play() override { pHdmi->play(); }
private : HDMI *pHdmi; };
int main() { Computer computer;
Device01 dev1; computer.playVideo(&dev1);
Device02 dev2; VGAToHDMIAdapter adapter(&dev2); computer.playVideo(&adapter);
return 0; }
|
程序运行的输出结果如下:
1 2
| 使用 VGA 接口的投影仪播放视频 使用 HDMI 接口的投影仪播放视频
|
装饰器模式
装饰器模式的概念
装饰器模式(Decorator Pattern)是一种结构型设计模式,允许在不改变对象结构的前提下,动态地为对象添加额外功能。就像是给一个盒子套上漂亮的包装纸,同时还可以叠加多个包装(功能),而不会改变盒子本身。简而言之,装饰器模式的主要作用是动态地给对象 “增强功能”,避免使用继承导致类爆炸式增长。
装饰器模式的结构组成
- 抽象组件(Component):定义对象的接口(可以是抽象类或接口)。
- 具体组件(ConcreteComponent):被装饰的原始对象,提供基本功能。
- 抽象装饰器(Decorator):持有一个组件对象引用,并实现相同接口。
- 具体装饰器(ConcreteDecorator):在原始功能的基础上添加新功能。
装饰器模式的优缺点
- 优点:
- 灵活:可以动态组合不同功能。
- 遵循 “开闭原则”:对扩展开放,对修改封闭。
- 可以用于替代复杂的继承体系。
- 缺点:
- 多层装饰时,调试会变困难,结构可能会变复杂。
- 如果组合太多层,可能会降低性能和代码可读性。
装饰器模式的使用场景
- 想要在运行时为对象动态添加功能。
- 不想使用继承方式来扩展类(避免子类爆炸)。
- 想要通过多个组合方式来构建不同功能的对象。
装饰器模式的使用
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
|
#include <iostream>
using namespace std;
class Coffee {
public: virtual string getDescription() = 0;
virtual double cost() = 0;
virtual ~Coffee() {}
};
class BasicCoffee : public Coffee {
public: string getDescription() override { return "Basic Coffee"; }
double cost() override { return 5.0; }
};
class CoffeeDecorator : public Coffee {
protected: Coffee *decoratedCoffee;
public: CoffeeDecorator(Coffee *coffee) : decoratedCoffee(coffee) {}
};
class Milk : public CoffeeDecorator {
public: Milk(Coffee *coffee) : CoffeeDecorator(coffee) {}
string getDescription() override { return decoratedCoffee->getDescription() + " + Milk"; }
double cost() override { return decoratedCoffee->cost() + 1.0; }
};
int main() { Coffee *basicCoffee = new BasicCoffee(); Coffee *milk = new Milk(basicCoffee); cout << milk->getDescription() << endl; cout << milk->cost() << endl;
delete milk; delete basicCoffee; }
|
程序运行的输出结果如下: