我们将23种经典设计模式分为了三类,分别是创建型,结构型和行为型,今天,我们将这三种类型分别设为三个小模块,逐一回顾设计模式的原理 实现 设计意图和应用场景

1.创建型设计模式

包括了单例模式,工厂模式 建造者模式 原型模式 主要是为避免对象的创建问题,封装复杂的创建问题,解耦代码的创建和使用

(1)单例模式

我们用来创建一个全局唯一的对象,一个类只能创建一个对象,这个类就是一个单例类,这个设计模式就是单例设计模式,单例的实现方式有 饿汉式 懒汉式 双重检测 静态内部类 枚举

但是单例并不很好,因为有一些缺点

1.单例对于OOP的支持不好

2.单例会隐藏类之间的依赖关系

3.单例对于代码的扩展性并不好

4.单例会导致可测试性不好

5.单例不支持有参数的构造函数

那么如何解决单例的问题?我们可以使用一些其他的方案去代替到哪里模式 ,工厂模式 , IOC容器

当然如果一个类本身没有扩展需求,又不依赖于外部系统,设计为到哪里也很好的

(2)工厂模式

包含简单工厂 工厂方法, 抽象工厂三种细分的模式,其中简单工厂和工厂方法很常用,抽象工厂少见

工厂模式用来创建继承同一父类或者接口的一组子类,由一组给定的参数来确定生成那种对象,实际上,如果创建的对象逻辑不复杂,直接new就好了,只有够复杂,才是用工厂模式

对于创建逻辑比较简单的情况,我们使用简单工厂,只是封装创建过程,放在一个工厂类中就可以了,如果创建的逻辑比较复杂的话,我们可以使用工厂方法模式,将创建逻辑拆分的更加细致

对于是否使用工厂模式,我们可以从下面的4个维度考虑,封装变化,代码复用,隔离复杂性,控制复杂度

(3)建造者模式

用于创建复杂对象,通过设置不用的可选参数,来定制化的创建不同的独享,建造者的原理很简单

主要是为了

将类的必填属性放在构造函数,在强制创建对象的时候就会设置,如果必填的属性很多,就将这些必填属性都放在构造函数中设置,构造函数会出现很长的问题,如果set,那么就不能进行创建时校验的问题了

如果类的属性之间有一定的约束关系,就不能set了,只能构造时候去安放

如果我们希望创建不可变的对象,也就是对象创建好了,就不能改变其中的内部属性,就不能暴露set方法

(4)原型模式

如果对象的创建成本比较大,而同一个类的不同对象之间差别不大,就可以利用现有的对象的进行复制的方法,来创建新的对象,节省创建时间的目的,这种基于原型来创建对象的方式叫做原型模式

原型模式的实现可以使用深拷贝和浅拷贝,浅拷贝只复制对象的引用,和一些基本数据类型的内存地址,深拷贝则是完完全全的创建了一个独立对象

如果拷贝的对象是不可变的对象,浅拷贝是没有问题的,但是对于可变的对象来说,浅拷贝存在着修改数据的问题,所以建议一般使用深拷贝的方式

结构性设计模式,就是利用类或者对象组合在一起的经典结构,这些经典结构来解决特定的应用场景,结构型模式常见的有,代理模式 桥接模式 装饰器模式 适配器模式 门面模式 组合模式 享元模式

(1)代理模式

代理模式在不改变原始类接口的条件,为原始类定义一个代理类,去为了开发新的功能,这不是去增强某些功能,这就是和装饰器模式最大的不同,一般模式,我们让代理类代码和原始类实现相同的接口然后在此基础上,我们加入一些新的功能并且我们不事先为每个原始类编写代理类,在运行的的时候动态的创建代理类来替换原始类

这种开发经常用于开发一些非功能性需求,比如监控 统计 鉴权 限流 事务 幂等

(2)桥接模式

实现并不困难,但是并不容易理解,而且应用场景也比较有效,所以桥接模式的使用并不常见,

常见的理解方式有两种,一是将抽象和实现解耦让其可以独立开发,而是使用组合优于继承,理解更加通用,不管哪种理解模式,都是类之间的组合关系

对于第一种的额理解,弄懂其中的抽象和实现的概念,是其关键

抽象出来了一套类库,只是包含了骨架代码,真正的业务逻辑交给了实现来完成,其中的实现也并非真正的实现类,而是一套独立的类库,利用组合的方式放一起

(3)装饰器模式

用户解决关系过于复杂的问题,通过组合来替代继承,给原始类增加新的功能,而且可以嵌套的使用多个装饰器,这样就需要装饰器类和原始类具有相同的功能

(4)适配器模式

装饰器模式 代理模式,都是提供的原始类相同的接口,但是适配器提供了和原始类不同的接口,适配器模式是用于做适配的,将不兼容的接口变为可兼容的接口,用于补救设计上缺陷

其常见的场景有

封装有缺陷的接口

统一多个类的接口设计

替换依赖的外部系统

兼容老版本接口

适配不同格式的数据

(5)门面模式

门面模式原理 实现都非常的简单,应用场景比较明确,封装细粒度的接口,提供组合各个细粒度接口的高层次接口,提高接口的易用性,解决性能,分布式事务的问题

(6)组合模式

组合模式和我们讲过的组合关系是两码事,这里的组合模式是处理树形数据结构,数据必须能表示为树形结构,才能使用组合模式

这与其说是一种设计模式,不如说是对业务场景的一种数据结构和算法的抽象,比如数据可以表示为树这种数据结构,业务需求可以在树上的递归遍历来实现

(7)享元模式

就是讲多个对象设计为不可变的对象

在一个系统中存在着大量的重复对象,可以利用享元模式,将对象设计为享元,内存中只保留一份实力,供多出代码引用,减少内存中对象的数量,节省内存的目的,实际上只要是相似的部分,也可以提取出来做享元

行为型设计模式

可以分为 观察者设计模式 ,模板模式,策略模式,职责链模式,迭代器模式,状态模式,访问者模式,备忘录模式,命令模式,解释器模式,中介模式

(1).观察者模式

将观察者和被观察者解耦,观察者模式的应用场景很广泛,小到代码的解耦,大到架构层面的系统解耦

不同的应用场景和需求下,有不同的实现的方式,有同步阻塞的实现方式,有异步非阻塞的实现方式,同时有进程的实现方式,也有跨进程的实现方式,同步阻塞是最经典的实现方式,为了给代码解耦,也有异步非阻塞的实现方式,还能提高代码效率,进程之间的观察者更加解耦,基于了消息队列,来做到不同进程之间的观察者和被观察者的交互

(2)模板模式

在一个方法中定义一个算法骨架,将某些步骤推迟到子类中实现,让其在不改变整体的结构的情况下,重新实现这些步骤,这些算法,可以理解为广义上的业务逻辑,并不特指某些算法

模板模式常用的就是复用和扩展,提供预留的扩展点,让框架的用户在不改变原有的代码执行情况下,定制化框架功能点

相比较于普通的函数调用,回调是一种双向的调用关系,A先注册函数F到B,A调用B的时候,B调用F回传给A

(3)策略模式

策略模式定义一组算法类,将每一个算法封装起来,让其可以相互替换,策略模式让算法的变化独立于使用的客户端,解耦策略的定义 创建 使用,

策略模式的定义很简单,包含一个策略接口和一组实现的策略类,策略的创建交给工厂类,客户端选择使用哪个策略,可以有两种选择方式,编译时静态决定和运行时动态确定,静态确定很少见,一般是动态确定,实际的开发中,策略模式很常见,其作用为让其不至于过于复杂,代码量过多

(4)职责链模式

让多个处理器依次处理一个请求,一个请求经过了A处理器处理后,在传给了B,依次传递,形成一个链条,链条上每一个处理器都能够各自承担自己的处理责任

一般来说,只要有一个处理器能够处理这个请求,就不会继续传递了,但是实际开发中,一直会走一遍的存在

职责链模式用于常用0框架开发,用于实现过滤器,拦截器功能,让框架的使用者在不修改源码的情况下,添加新的功能

(5)迭代器模式

游标模式,遍历集合对象,就是包含一组对象的对象,比如数组 链表 图 树 跳表

迭代器模式主要作用是解耦容器开发和遍历代码,

迭代器可以封装内部的复杂数据结构

可以将遍历的操作从集合类汇总拆分出来

可以让添加新的遍历算法更加容易,因为是一个外部类

通过迭代器遍历集合的时候,如果进行了增加或者删除集合的元素,可能导致出错,于是可以有两种解决方案,1是遍历的时候不能增删元素,另一种是增删后报错,就是fail-fast方案

(6)状态模式

用于实现状态机,常用于游戏 工作流引擎等,分别包含 状态 事件 动作,事件可以被称为状态转移,不过动作不是必须的,可以只转移状态

对于状态机,我们总结了三种实现方式

1,分支逻辑法,使用if-else和switch-case分支逻辑,实现方式最简单,最直接

2.查表法,状态很多,转移复杂的状态机来说,查表法合适,利用二维数组来表示状态转移图,能够提高代码的可维护性

3.状态模式,对于状态转移容易,但是事件触发的动作比较复杂的状态机来说,首选这种

(7)访问者模式

解耦操作和对象本身,保持类职责单一,满足开闭原则和代码的复杂性

对于访问者模式,学习的主要难点在代码实现,代码实现比较复杂的主要原因是,函数重载在大部分面向对象编程语言中静态绑定,于是根据参数的实际类型决定,所以这就说道了Double Dispatch和Double Dispatch,不需要访问者模式

(8)备忘录模式

又叫做快照模式,具体来说,在不违背封装原则的前提下,捕获一个对象的内部状态,在对象之外的保存这个状态,以便之后恢复对象为先前的状态,这个模式的定义这两个部分的内容,一部分是存储后以便后面的恢复,另一部分,要在不违背封装的前提下,进行对象的备份和恢复

备忘录的应用场景比较明确和有限,主要防丢失和插销和恢复,他和平时我们说的备份相似,不过,两者的区别在于,备忘录模式更侧重于代码的设计和实现,对于大对象备份,备份占用的存储空间会比较大,备份和恢复消耗会比较长,针对这个问题,不同的业务场景有不同的处理方式,比如只备份必要的恢复信息,结合最新的数据来恢复,再比如,全量备份或者增量备份相结合

(9)命令模式

命令模式在平时工作中不常用,稍微了解下就可以了

落实在编码实现,就是讲函数封装为对象,我们知道,在大部分的编程语言汇总,函数没法参数传递给别的函数,没法赋值给变量,借助命令模式,我们将函数封装为对象,可以将函数当做对象使用

(10)解释器模式

用于解释某个语言定义的语法,并定义一个解释器用来处理这个语法,实际上,这里的语言不仅仅指我们说的各种语言,从广义上来说,能够承载语言的载体,都可以是语言

要想要了解语言来表达信息,我们定义了对应的语法规则,书写者就可以根据语法规则来书写句子,阅读者根据语法规则来阅读句子,这样才能做到信息的正确传递

解释器的代码比较灵活,没有固定的模板,设计模式主要应对代码的复杂性,解释器不例外,一般的做法是,将语法规则拆分为一些小的独立的单元,然后对单元进行解析,最终合并为整个语法规则的解析

(11)中介模式

引入了中介这个中间层,将一组对象之间的交互关系从多对多转为了一对多,原来一个对象跟n个对象交互,原本和对象的只有一个,简化了代码的复杂性,提高了代码的可独享和可为华星

发表评论

邮箱地址不会被公开。 必填项已用*标注