设计模式(一):模板方法、观察者模式、策略模式
文章目录
- 1. 了解设计模式
- 2. 如何学习设计模式
- 3. 设计原则
- 3.1 依赖倒置原则
- 3.2 开放密闭原则
- 3.3 面向接口原则
- 3.4 封装变化点原则
- 3.5 单一职责原则
- 3.6 里氏替换原则
- 3.7 接口隔离原
- 3.8 组合继承原则
- 4. 设计模式
- 4.1 模板方法
- 4.2 观察者模式
- 4.3 策略模式
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 : public ZooShow {protected:virtual bool Show0(){cout << "show1" << endl;return true;}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 ZooShowEx3;// ZooShow *zs1 = new ZooShowEx1;// ZooShow *zs2 = new ZooShowEx2;zs->Show();return 0;
}
4.2 观察者模式
定义
定义对象间的一种一对多(变化)的依赖关系,以便当一个对象(Subject)的状态发生改变时,所有依赖于它的对象都得到通知并自动更新。
稳定点:“一”对“多”的依赖关系,"一"的状态发生变化,“多”的状态跟着变化。
变化点:“多”增加,“多”减少。
使用场景
气象站发布气象资料给数据中心,数据中心经过处理,将气象信息更新到两个不同的显示终端(A 和B)。
稳定点:数据中心处理数据,将数据更新到显示终端。
变化点:终端的增加(Attach)、终端的减少(Detach)。
注意细节
- 面向接口编程:不同的终端设备都继承同一个类IDisplay,不同的终端重写基类的虚函数show().
- 接口隔离:DisplayA 与 DataCenter是通过一个抽象的数据结构List联系起来。
- 封装变化点:“多”增加–Attach “多”减少–Detach
- 使用者如何调用:实现一个类继承至基类IDisplay,重写基类的虚函数。终端的添加与删除,调用接口Attach() 与 Detach(),最后,再定时调用或事件触发调用接口Notify()。
代码实现
#include <vector>
//符合的设计原则:
//面向接口编程:IDisplay类的设计
//接口隔离:类与类之间的隔离,DisplayA 与 DataCenter是通过一个抽象的数据结构List联系起来。
//封装变化点:“多”增加--Attach “多”减少--Detachclass IDisplay { //不将变量类型声明为某个特定的具体类,而是声明为某个接口
public://客户程序无需获知对象的具体类型,只需要知道对象所具有的接口virtual void Show(float temperature) = 0;virtual ~IDisplay() {}
};class DisplayA : public IDisplay {public:virtual void Show(float temperature);
private:void jianyi();
};class DisplayB : public IDisplay{public:virtual void Show(float temperature);
};class DisplayC : public IDisplay{public:virtual void Show(float temperature);
};class WeatherData {};class DataCenter {public:void Attach(IDisplay * ob);void Detach(IDisplay * ob);void Notify() {float temper = CalcTemperature();for (auto iter = obs.begin(); iter != obs.end(); iter++) {(*iter)->Show(temper);}}// 接口隔离
private:virtual WeatherData * GetWeatherData();virtual float CalcTemperature() {WeatherData * data = GetWeatherData();// ...float temper/* = */;return temper;}std::vector<IDisplay*> obs;
};int main() {DataCenter *center = new DataCenter;IDisplay *da = new DisplayA();IDisplay *db = new DisplayB();IDisplay *dc = new DisplayC();center->Attach(da);center->Attach(db);center->Attach(dc);center->Notify();//-----center->Detach(db);center->Notify();return 0;
}
4.3 策略模式
定义
定义一系列算法,把它们一个个封装起来,并且使它们可互相替换。该模式使得算法可独立于使用它的客户程序而变化。
稳定点:客户程序与算法的调用关系
变化点:新增算法,算法内容的改变
使用场景
某商场节假日有固定促销活动,为了加大促销力度,现提升国庆节促销活动规格。
稳定点:促销活动
变化点:不同的节假日、促销活动的算法。
实现细节
1.接口隔离:类Promotion中声明类ProStategy的对象s
,通过构造函数对象s
初始化,从而类Promotion中可以调用类ProStategy的接口,解决两个类的依赖关系。
2.开闭原则:实现一个类ProStategy,通过继承和重写基类的虚函数,实现对基类的拓展。
3.面向接口编程:ProStategy类的设计。
4.使用者如何调用:
int main () {Context ctx;ProStategy *s = new VAC_QiXi1();Promotion *p = new Promotion(s);p->CalcPromotion(ctx);return 0;
}
代码实现
//符合的设计原则:
//接口隔离:通过依赖注入,解决两个类的依赖关系
//开闭原则:对拓展开放,对修改关闭
//面向接口编程:ProStategy类的设计
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;
}
推荐一个零声学院免费公开课程,个人觉得老师讲得不错,分享给大家:Linux,Nginx,ZeroMQ,MySQL,Redis,fastdfs,MongoDB,ZK,流媒体,CDN,P2P,K8S,Docker,TCP/IP,协程,DPDK等技术内容,点击立即学习:
设计模式(一):模板方法、观察者模式、策略模式相关推荐
- 设计模式之模板方法和策略模式的区别(一)
模板方法: 定义一个算法的大纲,而由其子类定义其中某些步骤的内容.而其算法的个别步骤可以有不同的实现细节.算法结构依然维持不变.用继承的方式改变算法中的具体步骤,依赖程度高,算法在父类(父类是抽象类) ...
- 设计模式初学者系列-策略模式 -------为什么总是继承
设计模式初学者系列-策略模式 -------为什么总是继承 模板方法的延续 这篇稿子是基于我的前一篇模板 ...
- 老王讲设计模式(一)——策略模式
策略模式属于对象的行为模式.其用意是针对一组算法,将每一个算法封装到具有共同接口的独立的类中,从而使得它们可以相互替换.策略模式使得算法可以在不影响到客户端的情况下发生变化. 老王最近接到一个工作,上 ...
- 设计模式(三)策略模式——在Spring中使用策略模式
前言 在 设计模式(一)策略模式 -- 策略模式结构 和 设计模式(二)策略模式 -- 在程序中通过枚举使用策略模式 两篇博文中分析了策略模式的基础使用,在实际的项目开发中要结合spring容器使用策 ...
- 设计模式 by Python1:策略模式
设计模式 by Python1:策略模式 最近开始重新看<Head First 设计模式>,作为一个不错的练习,打算在整理设计模式笔记的时候用Python实现. 作为第一个介绍的设计模式, ...
- C++设计模式学习笔记:策略模式
C++设计模式学习笔记:策略模式 策略模式设计商场促销 1.策略模式介绍 2.商场收银系统策略模式实现 3.策略模式与工厂模式结合 3.策略模式与工厂模式对比 策略模式设计商场促销 1.策略模式介绍 ...
- 设计模式解读之一: 策略模式——鸭子游戏
设计模式解读之一: 策略模式--鸭子游戏 当我们掌握了Java的语法,当我们了解了面向对象的封装.继承.多态等特性,当我们可以用Swing.Servlet.JSP技术构建桌面以及Web应用,不意味着我 ...
- lt;二gt;读lt;lt;大话设计模式gt;gt;之策略模式
又和大家见面了.可以坚持写出第二篇文章真不错,好好加油. <<大话设计模式>>解说策略模式是以商场收银软件程序开头的,那么问题来了.哪家商场收银软件强,开玩笑了. 读过上篇文章 ...
- 【HeadFirst 设计模式总结】1.策略模式
1.书中举了一个鸭子类的设计,有些会飞或者会叫,有些不会飞可能也不会叫,用继承则导致不该有的功能通过继承而继承了下来,使用接口则代码无法做到最大程度的重用.进而引出设计原则1:找出应用中可能需要变化之 ...
- 设计模式学习笔记--Strategy 策略模式
所谓策略模式(Strategy Pattern),就是将策略 (算法) 封装为一个对象,易于相互替换,如同 USB 设备一样可即插即用:如果将策略.具体的算法和行为,编码在某个类或客户程序内部,将导至 ...
最新文章
- web客户端 http error 413
- python斑点检测
- docker的网络模式
- 使用JBoss Cool Store的终极云零售指南
- java对外发布接口文档_java之接口文档规范
- Python正则表达式练习
- [渝粤教育] 西南科技大学 现代企业管理 在线考试复习资料(2)
- 【翻译】 Video Object Tracking using Improved Chamfer Matching and Condensation Particle Filter
- 数学建模--层次分析法
- sql优化工具explain的使用
- IDEA快速启动ssm项目
- 从HttpClient3迁移到HttpClient4
- 从零学物联网技术:较大影响了我们生活的物联网技术应用有哪些?
- 并发编程之CompletableFuture全网最细最全用法(一)
- 前端-获取treegrid的选中数据
- itextpdf对PDF文件进行签名
- 喜讯!湖南云畅网络科技有限公司成为百度核心生态伙伴! 共建智慧交通新生态
- 自动化办公——PPT免费模板(强力推荐)
- 【MySQL】字符集utf8mb4无法存储表情踩坑记录
- JDK 内置命令行工具:工欲善其事,必先利其器