文章目录
1. 了解设计模式
在学习设计模式之前,首先我们要先知道设计模式是什么、设计模式是怎么来的、设计模式解决了什么问题、设计模式的基础。
1. 设计模式是解决问题的固定套路,它是一种抽象的思想,熟练运用设计模式需要具备一定的工程代码量。
2. 设计模式是在满足设计原则的基础上慢慢迭代而来的。
3. 设计模式用来解决既有稳定点,又有变化点的问题。期望修改少量的代码,就可以适应需求的变化。
4. 面向对象思想是设计模式的基础。
封装:隐藏代码细节、实现功能模块化;继承:无需修改原有类的情况下通过继承实现功能的拓展;多态:动态多态(继承中虚函数的重写)与静态多态(函数的重载)。
2. 如何学习设计模式
1. 明确目的:在现有设计模式的基础上,拓展代码(新手程序员);做功能抽象时,选择设计模式。
2. 学习步骤:
1. 该设计模式解决什么问题,找稳定点和变化点
2. 该设计模式的代码结构是什么
3. 该设计模式符合哪些设计原则
4. 如何在上面拓展代码
5. 该设计模式有哪些应用场景
3. 设计原则
3.1 依赖倒置原则
高层模块不应该依赖低层模块,两者都应该依赖抽象
抽象不应该依赖具体实现,具体实现应该依赖于抽象
自动驾驶系统公司是高层,汽车生产厂商为低层,它们不应该互相依赖,一方变动另一方也会跟着变动;而应该抽象一个自动驾驶行业标准,高层和低层都依赖它;这样以来就解耦了两方的变动;
自动驾驶系统、汽车生产厂商都是具体实现,它们应该都依赖自动驾驶行业标准(抽象)
3.2 开放密闭原则
一个类应该对扩展(组合和继承)开放,对修改关闭
3.3 面向接口原则
不将变量类型声明为某个特定的具体类,而是声明为某个接口
客户程序无需获知对象的具体类型,只需要知道对象所具有的接口
减少系统中各部分的依赖关系,从而实现“高内聚、松耦合”的类型设计方案
3.4 封装变化点原则
将稳定点和变化点分离,扩展修改变化点;让稳定点和变化点的实现层次分离。
3.5 单一职责原则
一个类应该仅有一个引起它变化的原因
3.6 里氏替换原则
子类型必须能够替换掉它的父类型;主要出现在子类覆盖父类实现,原来使用父类型的程序可能出现错误;覆盖了父类方法却没有实现父类方法的职责。
3.7 接口隔离原
不应该强迫客户依赖于它们不用的方法
一般用于处理一个类拥有比较多的接口,而这些接口涉及到很多职责
3.8 组合继承原则
继承耦合度高,组合耦合度低
4. 设计模式
4.1 模板方法
定义
定义一个操作中的算法的骨架 ,而将一些步骤延迟到子类中。 Template Method使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
稳定点:算法骨架 变化点:子流程需要变化
使用场景
某个品牌动物园,有一套固定的表演流程,但是其中有若干个表演子流程可创新替换,以尝试迭代更新表演流程。
注意细节:
- 父类使用 protected 保护子类需要复写的子流程,这样子类的子流程只能父类来调用。
- 父类通过虚函数来实现子类需要复写的子流程
- 子类通过继承来复写父类的子流程,父类通过多态来调用子类复写的流程。
代码实现
#include <iostream>
using namespace std;
//满足的设计原则:
//单一职责:一个类只实现了一个功能ZooShow
//开闭原则:对拓展开放,对修改关闭
//依赖倒置:子类拓展时,需要依赖基类的虚函数实现;使用者只依赖接口Show()
//封装变化点:protected修饰,变化点只对子类拓展可见,对使用者不可见
//接口隔离:只提供使用者需要的接口,不需要的接口对使用者不可见
// 不满足开闭原则 对扩展开放 继承(虚函数覆盖)
// 扩展功能:继承 (虚函数覆盖) 组合
class ZooShow {
public:
void Show() {
if (Show0())
PlayGame();
Show1();
Show2();
Show3();
}
private:
void PlayGame() {
cout << "after Show0, then play game" << endl;
}
//封装变化点:父类使用 protected 保护子类需要复写的子流程
protected:
virtual bool Show0(){
cout << "show0" << endl;
return true;
}
virtual void Show2(){
cout << "show2" << endl;
}
virtual void Show1() {
}
virtual void Show3() {
}
};
//子类通过继承来复写父类的子流程
class ZooShowEx1