C++设计模式大全,23种设计模式合集详解—👉(点我跳转)
一、设计流程探讨
一天,你打算为游客们创建一款导游程序。该程序的核心功能是提供美观的地图,以帮助用户在任何城市中快速定位。
用户期待的程序新功能是自动路线规划:他们希望输入地址后就能在地图上看到前往目的地的最快路线。
程序的首个版本只能规划公路路线。驾车旅行的人们对此非常满意。但很显然,并非所有人都会在度假时开车。因此你在下次更新时添加了规划步行路线的功能。此后,你又添加了规划公共交通路线的功能。
而这只是个开始。不久后,你又要为骑行者规划路线。又过了一段时间,你又要为游览城市中的所有景点规划路线。
尽管从商业角度来看,这款应用非常成功,但其技术部分却让你非常头疼:每次添加新的路线规划算法后,导游应用中主要类的体积就会增加一倍。终于在某个时候,你觉得自己没法继续维护这堆代码了。
无论是修复简单缺陷还是微调街道权重,对某个算法进行任何修改都会影响整个类,从而增加在已有正常运行代码中引入错误的风险。
此外,团队合作将变得低效。如果你在应用成功发布后招募了团队成员,他们会抱怨在合并冲突的工作上花费了太多时间。在实现新功能的过程中,你的团队需要修改同一个巨大的类,这样他们所编写的代码相互之间就可能会出现冲突。
解决方案:
策略模式建议找出负责用许多不同方式完成特定任务的类,然后将其中的算法抽取到一组被称为策略的独立类中。
名为上下文的原始类必须包含一个成员变量来存储对于每种策略的引用。上下文并不执行任务,而是将工作委派给已连接的策略对象。
上下文不负责选择符合任务需要的算法——客户端会将所需策略传递给上下文。实际上,上下文并不十分了解策略,它会通过同样的通用接口与所有策略进行交互,而该接口只需暴露一个方法来触发所选策略中封装的算法即可。
因此,上下文可独立于具体策略。这样你就可在不修改上下文代码或其他策略的情况下添加新算法或修改已有算法了。
在导游应用中,每个路线规划算法都可被抽取到只有一个 build¬Route生成路线方法的独立类中。该方法接收起点和终点作为参数,并返回路线中途点的集合。
即使传递给每个路径规划类的参数一模一样,其所创建的路线也可能完全不同。主要导游类的主要工作是在地图上渲染一系列中途点,不会在意如何选择算法。该类中还有一个用于切换当前路径规划策略的方法,因此客户端(例如用户界面中的按钮)可用其他策略替换当前选择的路径规划行为。
真实世界类比:
假如你需要前往机场。你可以选择乘坐公共汽车、预约出租车或骑自行车。这些就是你的出行策略。你可以根据预算或时间等因素来选择其中一种策略。
二、模式介绍
该模式与 Strategy模式(策略模式) 非常像,可以不必太计较它们之间的差异,只需要关注该模式怎么去解决稳定与变化的问题就行。
(1)模式动机
在软件构建过程中,某些对象的状态如果改变,其行为也会随之而发生变化,比如文档处于只读状态,其支持的行为和读写状态支持的行为就可能完全不同。
如何在运行时根据对象的状态来透明地更改对象的行为,而不会为对象操作和状态转化之间引入紧耦合?
(2)模式定义
允许一个对象在其内部状态改变时改变它的行为,从而使对象看起来似乎修改了其行为。
(3)要点总结
a). State模式将所有与一个特定状态相关的行为都放入一个State的子类对象中,在对象状态切换时,切换相应的对象,但同时维持State的接口,这样实现了具体操作与状态转换之间的解耦。
b). 为不同的状态引入不同的对象使得状态转换变得更加明确,而且可以保证不会出现状态不一致的情况,因为转换是原子性的——即要么彻底转换过来,要么不转换。
c). 如果State对象没有实例变量,那么各个上下文可以共享同一个State对象,从而节省对象开销。
三、代码实现
以下是未使用状态模式时的代码,可以发现就如上探讨所说,每次增加个新状态都要修改主体类。
enum NetworkState{
Network_Open,
Network_Close,
Network_Connect,
Network_Wait //新增的状态
};
class NetworkProcessor{ //主体类
NetworkState state;
public:
void Operation1(){
// 当出现大量 if-else 或 swith-case 时考虑策略模式或状态模式
if (state == Network_Open) {
//...进行一系列操作
state = Network_Close;
}
else if (state == Network_Close) {
//...进行一系列操作
state = Network_Connect;
}
else if (state == Network_Connect) {
//...进行一系列操作
state = Network_Wait;
}
else if (state == Network_Wait) { //因状态需新增而新增操作
//...进行一系列操作
}
}
void Operation2() {
if (state == Network_Open) {
//...进行一系列操作
state = Network_Close;
}
else if (state == Network_Connect) {
//...进行一系列操作
state = Network_Close;
}
else if ((state == Network_Wait) { //因状态需新增而新增操作
//...进行一系列操作
}
}
void Operation3() {
//...与 Operation1 和 Operation2 同理
}
};
使用了状态模式后,我们只需要根据新增的状态通过继承与多态的方式去扩展子类,并不需要去修改主体类。
class NetworkState{
public:
NetworkState* pNext; //在这里为了简单实现为公有的,实际上要为私有的并实现其 get 与 set 的方法
virtual void Operation1() = 0;
virtual void Operation2() = 0;
virtual void Opepration3() = 0;
virtual ~NetworkState(){}
};
class NetworkProcessor{
NetworkState* pState;
public:
NetworkProcessor(NetworkState* pState){
this->pState = pState;
}
void Operation1(){
//...
pState->Operation1();
pState = pState->pNext; //在这里我们只知道有下一个状态
//...
}
void Operation2(){
//...
pState->Operation2();
pState = pState->pNext;
//...
}
void Operation3(){
//...
pState->Operation3();
pState = pState->pNext;
//...
}
};
class OpenState : public NetworkState{
static NetworkState* m_instance; //这里结合了单例设计模式
public:
static Networktate* getInstance(){
if (m_instance == nullptr) {
m_instance = new OpenState();
}
return m_instance;
}
void Operation1(){
//...进行一系列操作
pNext = CloseState::getInstance(); //自己去管理下一个状态的转换关系
}
void Operation1(){
//...进行一系列操作
pNext = ConnectState::getInstance(); //自己去管理下一个状态的转换关系
}
void Operation1(){
//...进行一系列操作
pNext = OpenState::getInstance(); //自己去管理下一个状态的转换关系
}
};
class CloseState : public NetworkState{
//...与OpenState相似
};
class ConnectState: public NetworkState{
//...与OpenState相似
};
class WaitState: public NetworkState{ //新增的状态,只需要扩展子类就行,并不需要修改主体类
//...与OpenState相似
};