20、STL设计模式:迭代器模式、适配器模式、策略模式、观察者模式
设计模式这东西,很多人觉得是Java的专利。其实C++里,尤其是STL,早就把几种核心模式玩得炉火纯青了。我个人习惯把STL看作一本「设计模式活教材」——你天天用vector、sort、function,背后全是模式在撑腰。
今天咱们就聊四个:迭代器、适配器、策略、观察者。嗯,都是STL里高频出现的。你想想看,如果你能看透这些模式,写出来的代码会干净很多。
迭代器模式:STL的血管
迭代器模式,说白了就是「统一遍历接口」。不管底层是数组、链表还是红黑树,你给我一套begin()/end(),我就能用for循环扫过去。
我在项目中遇到过最典型的场景:业务方给了个自定义容器,底层是跳表。我问他「支持迭代器吗?」他说「啥是迭代器?」……后来我帮他封装了一层迭代器,所有算法直接复用,省了至少200行重复代码。
// 标准迭代器用法
std::vector<int> vec = {1, 2, 3, 4, 5};
for (auto it = vec.begin(); it != vec.end(); ++it) {
std::cout << *it << " ";
}
// 自己实现一个简易迭代器
class MyContainer {
int data[10];
public:
using iterator = int*;
iterator begin() { return data; }
iterator end() { return data + 10; }
};
迭代器有五种分类:输入、输出、前向、双向、随机访问。STL算法会根据迭代器类型自动选择最优实现。比如sort要求随机访问迭代器,list就不行——因为它只支持双向。
适配器模式:让不兼容的接口握手
适配器模式,说白了就是「加一层包装」。你有一个接口A,我需要接口B,中间加个转换层就行。
STL里最典型的适配器就是stack、queue、priority_queue。它们底层默认用deque,但对外只暴露push/pop/top。你想想看,deque有20多个方法,stack只给你4个——这就是适配器在干活。
// stack 适配 deque
std::stack<int, std::deque<int>> stk;
stk.push(10);
stk.push(20);
std::cout << stk.top(); // 20
// 你也可以适配 vector
std::stack<int, std::vector<int>> stk2;
stk2.push(30);
还有迭代器适配器,比如reverse_iterator。你传一个正向迭代器,它给你反向遍历。底层就是包装了一下operator++变成operator--。
策略模式:算法的可插拔
策略模式,说白了就是「把算法当参数传进去」。STL里最典型的就是sort的第三个参数——比较器。
std::vector<int> v = {3, 1, 4, 1, 5, 9};
// 默认升序
std::sort(v.begin(), v.end());
// 策略:降序
std::sort(v.begin(), v.end(), std::greater<int>());
// 策略:自定义lambda
std::sort(v.begin(), v.end(), [](int a, int b) {
return a % 10 < b % 10; // 按个位数排序
});
你想想看,sort本身不关心你怎么比大小,它只负责「排序」这个流程。比较策略是外挂的。这就是策略模式的核心——把变化的部分封装起来,通过组合代替继承。
STL里还有std::function,它可以存储任意可调用对象。配合策略模式,你可以实现「运行时切换算法」。
// 运行时策略切换
std::function<bool(int, int)> comp;
if (need_ascending) {
comp = std::less<int>();
} else {
comp = std::greater<int>();
}
std::sort(v.begin(), v.end(), comp);
观察者模式:事件驱动的基石
观察者模式,说白了就是「发布-订阅」。一个对象状态变了,通知所有依赖它的对象。STL里没有直接实现观察者模式,但它的组件经常被用来搭建观察者系统。
我记得有一次做实时监控系统,需要监控多个数据源,数据变了要通知UI、日志、报警三个模块。用观察者模式,数据源是Subject,三个模块是Observer,代码结构非常清晰。
// 简易观察者模式(STL风格)
class Observer {
public:
virtual void update(int value) = 0;
virtual ~Observer() = default;
};
class Subject {
std::vector<std::weak_ptr<Observer>> observers;
public:
void attach(std::shared_ptr<Observer> obs) {
observers.push_back(obs);
}
void notify(int value) {
for (auto& wobs : observers) {
if (auto obs = wobs.lock()) {
obs->update(value);
}
}
}
};
STL里的std::signal和std::slot(C++11之前)也是观察者模式的体现。不过现代C++更推荐用std::function+回调列表来实现。
四种模式的关系
这四种模式不是孤立的。你想想看:
- 迭代器模式让容器和算法解耦
- 适配器模式让接口转换
- 策略模式让算法可替换
- 观察者模式让对象间通信解耦
它们共同的目标就是「高内聚、低耦合」。STL之所以强大,就是因为这些模式用得恰到好处。