何为设计模式
设计模式是指在软件开发中,经过验证的,用于解决在特定环
境下,重复出现的,特定问题的解决方案;
设计原则
依赖倒置
开放封闭
一个类应该对扩展(组合和继承)开放,对修改关闭;
面向接口
不将变量类型声明为某个特定的具体类,而是声明为某个接口;客户程序无需获知对象的具体类型,只需要知道对象所具有的接口;
减少系统中各部分的依赖关系,从而实现“高内聚、松耦合”的类型设计方案;
封装变化点
将稳定点和变化点分离,扩展修改变化点;让稳定点和变化点的实现层次分离;
单一职责
一个类应该仅有一个引起它变化的原因;
里氏替换
子类型必须能够替换掉它的父类型;主要出现在子类覆盖父类实现,原来使用父类型的程序可能出现错误;覆盖了父类方法却没有实现父类方法的职责;
接口隔离
不应该强迫客户依赖于它们不用的方法;
一般用于处理一个类拥有比较多的接口,而这些接口涉及到很多职责;
客户端不应该依赖它不需要的接口。一个类对另一个类的依赖应该建立在最小的接口上。
组合优于继承
继承耦合度高,组合耦合度低;
模板方法
要点
- 最常用的设计模式,子类可以复写父类子流程,使父类的骨架流
程丰富; - 反向控制流程的典型应用;
- 父类 protected 保护子类需要复写的子流程;这样子类的子流程
只能父类来调用;
案例理解
某个品牌动物园,有一套固定的表演流程,但是其中有若干个表演子流程可创新替换,以尝试迭代更新表演流程;
变化点:子任务迭代,稳定点:父类骨架不变即对外接口不变
#include <iostream>
using namespace std;
// 开闭
class ZooShow {
public:
void Show() {
// 如果子表演流程没有超时的话,进行一个中场游戏环节;如果超时,直接进入下一个子表演流程
if (Show0())
PlayGame();
Show1();
Show2();
Show3();
}
private:
void PlayGame() {
cout << "after Show0, then play game" << endl;
}
bool expired;
// 对其他用户关闭,但是子类开放的
protected:
virtual bool Show0() {
cout << "show0" << endl;
if (! expired) {
return true;
}
return false;
}
virtual void Show2() {
cout << "show2" << endl;
}
virtual void Show1() {
}
virtual void Show3() {
}
};
// 框架
// 模板方法模式
class ZooShowEx10 : public ZooShow {
protected:
virtual void Show0() {
if (! expired) {
return true;
}
return false;
}
}
class ZooShowEx1 : public ZooShow {
protected:
virtual bool Show0() {
cout << "ZooShowEx1 show0" << endl;
if (! expired) { // 里氏替换
return true;
}
return false;
}
virtual void Show2(){
cout << "show3" << endl;
}
};
class ZooShowEx2 : public ZooShow {
protected:
virtual void Show1(){
cout << "show1" << endl;
}
virtual void Show2(){
cout << "show3" << endl;
}
};
class ZooShowEx3 : public ZooShow {
protected:
virtual void Show1(){
cout << "show1" << endl;
}
virtual void Show3(){
cout << "show3" << endl;
}
virtual void Show4() {
//
}
};
/*
*/
int main () {
ZooShow *zs = new ZooShowEx10; // 晚绑定还是早绑定
// ZooShow *zs1 = new ZooShowEx1;
// ZooShow *zs2 = new ZooShowEx2;
zs->Show();
return 0;
}
观察者模式
定义
定义对象间的一种一对多(变化)的依赖关系,以便当一个对
象(Subject)的状态发生改变时,所有依赖于它的对象都得到通
知并自动更新。 ——《 设计模式》 GoF
要点
- 观察者模式使得我们可以独立地改变目标与观察者,从而使二者
之间的关系松耦合; - 观察者自己决定是否订阅通知,目标对象并不关注谁订阅了;
- 观察者不要依赖通知顺序,目标对象也不知道通知顺序;
- 常用在基于事件的ui框架中,也是 MVC /QT的组成部分;
- 常用在分布式系统中、actor框架中;
应对稳定点,抽象
应对变化点,扩展(继承和组合)
本质
触发联动
代码背景
气象站发布气象资料给数据中心,数据中心经过处理,将气象
信息更新到两个不同的显示终端(A 和B);
#include <iostream>
#include <list>
#include <algorithm>
using namespace std;
//
class IDisplay {
public:
virtual void Show(float temperature) = 0;
virtual ~IDisplay() {}
};
class DisplayA : public IDisplay {
public:
virtual void Show(float temperature) {
cout << "DisplayA Show" << endl;
}
private:
void jianyi();
};
class DisplayB : public IDisplay{
public:
virtual void Show(float temperature) {
cout << "DisplayB Show" << endl;
}
};
class DisplayC : public IDisplay{
public:
virtual void Show(float temperature) {
cout << "DisplayC Show" << endl;
}
};
class DisplayD : public IDisplay{
public:
virtual void Show(float temperature) {
cout << "DisplayC Show" << endl;
}
};
class WeatherData {
};
// 应对稳定点,抽象
// 应对变化点,扩展(继承和组合)
class DataCenter {
public:
void Attach(IDisplay * ob) {
//
}
void Detach(IDisplay * ob) {
//
}
void Notify() {
float temper = CalcTemperature();
for (auto &ob : obs) {
ob->Show(temper);
}
}
// 接口隔离
private:
WeatherData * GetWeatherData();
float CalcTemperature() {
WeatherData * data = GetWeatherData();
// ...
float temper/* = */;
return temper;
}
std::list<IDisplay*> obs;
};
int main() {
// 单例模式
DataCenter *center = new DataCenter;
// ... 某个模块
IDisplay *da = new DisplayA();
center->Attach(da);
// ...
IDisplay *db = new DisplayB();
center->Attach(db);
IDisplay *dc = new DisplayC();
center->Attach(dc);
center->Notify();
//-----
center->Detach(db);
center->Notify();
center->Notify();
return 0;
}
策略模式
定义
定义一系列算法,把它们一个个封装起来,并且使它们可互相
替换。该模式使得算法可独立于使用它的客户程序而变化。
——《设计模式》 GoF
要点
策略模式提供了一系列可重用的算法,从而可以使得类型在运行
时方便地根据需要在各个算法之间进行切换;
策略模式消除了条件判断语句;也就是在解耦合;
本质
分离算法,选择实现;
代码背景
某商场节假日有固定促销活动,为了加大促销力度,现提升国
庆节促销活动规格;
class Context {
};
// 稳定点:抽象去解决它
// 变化点:扩展(继承和组合)去解决它
class ProStategy {
public:
virtual double CalcPro(const Context &ctx) = 0;
virtual ~ProStategy();
};
// cpp
class VAC_Spring : public ProStategy {
public:
virtual double CalcPro(const Context &ctx){
}
};
class VAC_Spring_v2 : public VAC_Spring {
public:
virtual double CalcPro(const Context &ctx){
//....
}
};
class VAC_worker : public ProStategy {
public:
virtual double CalcPro(const Context &ctx){}
};
// cpp
class VAC_QiXi : public ProStategy {
public:
virtual double CalcPro(const Context &ctx){}
};
class VAC_QiXi1 : public VAC_QiXi {
public:
virtual double CalcPro(const Context &ctx){}
};
// cpp
class VAC_Wuyi : public ProStategy {
public:
virtual double CalcPro(const Context &ctx){}
};
// cpp
class VAC_GuoQing : public ProStategy {
public:
virtual double CalcPro(const Context &ctx){}
};
class VAC_GuoQing2 : public VAC_GuoQing {
public:
virtual double CalcPro(const Context &ctx){}
};
class VAC_Shengdan : public ProStategy {
public:
virtual double CalcPro(const Context &ctx){}
};
// 设计原则:接口隔离原则
// 组合、继承
// 组合基类指针
// 两种方法:1. 采用具体接口选择算法 2. 依赖注入
class Promotion {
public:
Promotion(ProStategy *sss = nullptr) : s(sss){}
~Promotion(){}
void Choose(ProStategy *sss) {
// 条件选择
if (sss != nullptr) {
s = sss;
}
}
double CalcPromotion(const Context &ctx){
if (s != nullptr) {
return s->CalcPro(ctx);
}
return 0.0L;
}
private:
ProStategy *s;
};
int main () {
Context ctx;
ProStategy *s = new VAC_QiXi1(); //算法:变化
Promotion *p = new Promotion(s); //促销活动:稳定
p->Choose(new VAC_GuoQing2());
p->CalcPromotion(ctx);
return 0;
}