概述
设计模式是什么?
- 设计模式是在软件开发中,经过验证的,用于解决在特定环境下,重复出现的,特定问题的解决方案。
- 是解决问题的一些固定套路
- 慎用设计模式(精通设计模式要有一定的工程代码量)
设计模式怎么来的?
满足设计原则后,进而慢慢迭代出来的
解决了什么问题?
前提:具体的需求既有稳定点,又有变化点。
以下两种不需要使用设计模式:
- 全是稳定点(需求稳定)
- 全是变化点(例如:游戏开发)
设计模式期望来修改少量的代码,就可以适应需求的变化
一个比喻:
需求:整洁的房间,一只好动的毛,怎么保证环境的整洁?
方案:把猫关进笼子里
使用设计模式,让变化点在有限范围内变化。
设计模式基础
- 面向对象的思想
封装:隐藏实现细节,实现模块化
继承:无需修改原有类,通过继承实现对功能的扩展
多态:静态多态(函数重载)
动态多态(虚函数) - 设计原则
依赖倒置
开闭 对扩展开放,对修改关闭 封装,多态
面向接口 封装
封装变化点
单一职责 封装
里氏替换 多态
接口隔离
组合优于继承
最小知道原则 封装
如何学习设计模式?
明确目的
- 要懂得在现有基础上,扩展代码(公司已经有了)
- 做功能抽象时,如何选择设计模式
学习的步骤
- 设计模式解决了什么问题?(稳定/变化点)
- 该设计模式的代码结构是什么?
- 符合哪些设计原则 如何扩展代码?(针对变化点)
- 知道该设计模式的典型应用场景?(工作场景中/开源框架中)
设计模式
1. 模板方法
-
定义
定义一个操作中的算法的骨架 ,而将一些步骤延迟到子类中。
Template Method使得子类可以不改变一个算法的结构即可重
定义该算法的某些特定步骤。 -
解决的问题
稳定点:算法骨架
变化点:子流程需要变化 -
代码结构
基类中有骨架流程接口
所有子流程对子类开放并且是虚函数
多态的使用方式(基类指针指向子类的对象) -
符合的设计原则
单一职责
开闭
依赖倒置:
子类扩展时,需要依赖积累的虚函数
使用者只依赖接口
封装变化点: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;
}
- 如何扩展
实现子类继承基类,复写子流程
通过多态调用方式使用
2.观察者模式
-
定义
定义对象间的一种一对多(变化)的依赖关系,以便当一个对
象(Subject)的状态发生改变时,所有依赖于它的对象都得到通
知并自动更新。 -
解决的问题
稳定点:一对多的依赖关系(依赖关系是稳定的),一变化则多跟着变化
变化点: 多增加/多减少 -
代码结构
-
符合的设计原则
面向接口编程
接口隔离:类和类依赖于一个接口上
封装变化点: -
代码示例
// 背景:气象站发布气象资料给数据中心,数据中心经过处理,将气象
// 信息更新到两个不同的显示终端(A 和B);
#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 iter : obs) {
iter.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->Attach(dd);
center->Notify();
return 0;
}
- 如何扩展
增加一个类,继承
3. 策略模式
-
定义
定义一系列算法,把它们一个个封装起来,并且使它们可互相
替换。该模式使得算法可独立于使用它的客户程序而变化 -
解决了什么问题
稳定点:客户程序与算法的调用关系
变化点:新加算法/算法内容改变
3.代码结构
4.设计原则
接口隔离:依赖注入,一个接口解决两个类的依赖
- 示例代码
// 某商场节假日有固定促销活动,为了加大促销力度,现提升国
// 庆节促销活动规格;
//
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){}
};
// 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_Shengdan : public ProStategy {
public:
virtual double CalcPro(const Context &ctx){}
};
class Promotion {
public:
Promotion(ProStategy *sss) : s(sss){}
~Promotion(){}
double CalcPromotion(const Context &ctx){
return s->CalcPro(ctx);
}
private:
ProStategy *s;
};
int main () {
Context ctx;
ProStategy *s = new VAC_QiXi1();
Promotion *p = new Promotion(s);
p->CalcPromotion(ctx);
return 0;
}