概要理解设计模式
目录
创建型
工厂模式
主要解决接口选择问题,在任何需要生成复杂对象的地方,都可以使用工厂方法模式,简单对象不建议使用工厂方法。
场景:日志存储类型选择、数据库访问选择、连接服务器协议(pop3、imap、http等),都是把不同存储类型、数据库、协议作为产品类,共同实现一个接口
config = {“type”:“mysql”} dataBase = factory.getDb(config);
创业的时候,工厂只有一个产品衬衫,衬衫有白、黑、蓝三种色系。
生产衬衫时,只用工厂开门,收到订单后,工厂负责人确认生产哪款衬衫,工厂建立对应款式衬衫的生产流水线,就可以投入生产。
抽象工厂模式
加了抽象两字,意味着在工厂模式的基础上再进一步抽象,抽象什么呢?
随着工厂的盈利,工厂准备推出更多产品,除了衬衫,还有外套、裙子、帽子、鞋子、眼镜等产品,每个产品都有一些对应的款式,产品越来越多,规模越来越大,工厂考虑下设子工厂,每个子工厂只负责生产部分产品,有专门生产帽子的工厂,有专门生产鞋子的工厂,有专门生产裙子的工厂。
现在要生产衬衫,告诉总工厂生产负责人要生产具体的衬衫,总工厂根据实际需求建立衬衫工厂,衬衫工厂根据具体衬衫需求,建立对应款式的生产线,就可以生产衬衫了。
上面是为了更方便理解,使用了总工厂与子工厂的描述方式,而实际应用中,更多的是说:一个工厂里有一系列产品即产品族,每个产品再细分子类。生产一个子类,工厂(抽象工厂)先建立产品研发部门(具体工厂),产品研发部门研发具体子类产品(具体产品)
产品族的方式拓展方式相对比较困难
工厂方法与抽象工厂
工厂方法模式和抽象工厂模式不好分清楚,他们的区别如下:
工厂方法模式:
一个抽象产品类,可以派生出多个具体产品类。
一个抽象工厂类,可以派生出多个具体工厂类。
每个具体工厂类只能创建一个具体产品类的实例。
抽象工厂模式:
多个抽象产品类,每个抽象产品类可以派生出多个具体产品类。
一个抽象工厂类,可以派生出多个具体工厂类。
每个具体工厂类可以创建多个具体产品类的实例,也就是创建的是一个产品线下的多个产品。
区别:
工厂方法模式只有一个抽象产品类,而抽象工厂模式有多个。
工厂方法模式的具体工厂类只能创建一个具体产品类的实例,而抽象工厂模式可以创建多个。
工厂方法创建 “一种” 产品,他的着重点在于"怎么创建”,也就是说如果你开发,你的大量代码很可能围绕着这种产品的构造,初始化这些细节上面。也因为如此,类似的产品之间有很多可以复用的特征,所以会和模版方法相随。
抽象工厂需要创建一些列产品,着重点在于"创建哪些"产品上,也就是说,如果你开发,你的主要任务是划分不同差异的产品线,并且尽量保持每条产品线接口一致,从而可以从同一个抽象工厂继承。
对于java来说,你能见到的大部分抽象工厂模式都是这样的: —它的里面是一堆工厂方法,每个工厂方法返回某种类型的对象。
比如说工厂可以生产鼠标和键盘。那么抽象工厂的实现类(它的某个具体子类)的对象都可以生产鼠标和键盘,但可能工厂A生产的是罗技的键盘和鼠标,工厂B是微软的。
这样A和B就是工厂,对应于抽象工厂; 每个工厂生产的鼠标和键盘就是产品,对应于工厂方法;
用了工厂方法模式,你替换生成键盘的工厂方法,就可以把键盘从罗技换到微软。但是用了抽象工厂模式,你只要换家工厂,就可以同时替换鼠标和键盘一套。如果你要的产品有几十个,当然用抽象工厂模式一次替换全部最方便(这个工厂会替你用相应的工厂方法)
所以说抽象工厂就像工厂,而工厂方法则像是工厂的一种产品生产线
单例模式
-
单例类只能有一个实例
-
单例类必须自己创建自己的唯一实例
-
单例类必须为其他类提供这一实例
-
构造函数是私有的
场景:资源唯一,需要竞争的场景,文件管理、设备管理
懒汉+线程不安全:不加锁,效率高,不支持多线程
懒汉+线程安全:加锁,效率低,支持多线程,但实际99%情况不需要加锁
饿汉:效率高,浪费内存,线程不安全
java中考虑线程安全,php没有很好的支持多线程,基本不存在线程不安全问题
建造者模式
一个建造者使用多个简单的对象一步一步构造一个复杂的对象。
场景:套餐(FKC套餐、saas付费套餐)
与工厂模式的区别:建造者模式更关注零件转配顺序
原型模式
当直接创建对象开销比较大,且对象基本可以初始公用时,可以通过创建当前对象的克隆,相当于缓存该对象,可以提供性能
关键词:深拷贝、浅拷贝、序列化
场景:通过new产生一个对象需要繁琐的数据准备或访问权限,或资源开销很大,可以采用原型模式
结构型
适配器模式
适配器更像生活中的中转器,比如中国和欧洲充电器插头转换器,是为了适配中国和欧洲不同插头及电压
将一个接口转换成希望的另一个接口(输入与输出)
本质:对象引用其他对象,实现类似功能
桥接模式
把抽象化与实现化解耦
抽象类依赖实现类
比如多种图形 与 多种颜色 的组合,甚至更多种组合
本质:对象内引用其他对象,来完成特殊对象实现(通过桥接类,使用实现类),如JDBC驱动
组合模式
对象递归方式,对象里包含对象,说是组合,其实是对象内部引用对象,比如文件目录结构、树形结构、部门结构等
本质:对象内引用自身对象或其他对象
过滤器模式
一般不在23种模式中
被过滤数据对象、过滤操作对象
通过过滤操作对象 过滤 数据对象
本质:对象内使用其他对象
装饰器模式 Decotor
用一个类B去包装另一个类A,满足类A不改动的情况下,动态调整类B得到新的对象
本质:对象内引用其他对象
本质上和适配相似,适配是为了满足统一接口,装饰是为了灵活拓展接口,并保证旧接口稳定
外观模式 Facade
隐藏系统的复杂性,为底层接口提供更高层的一致的易用接口
底层接口相对更灵活,外观模式提供更高层的约束,提供比较严格的接口,使用简单保证开发一致
本质:类包装底层类,高层类实现底层类功能。隐藏底层类的功能细节
比如电脑,包装了cpu、内存条、主板、显示器、鼠标、键盘等对象,启动的时候,需要这些对象的配合启动
享元模式
将常用对象缓存在内存中,降低系统内存,提高使用效率
与数据结构缓存方式相似
代理模式
仅仅是代理,不改变原对象接口,不增强原对象接口
常用于安全控制、或复制对象开销大
-
1、和适配器模式的区别:适配器模式主要改变所考虑对象的接口,而代理模式不能改变所代理类的接口。
-
2、和装饰器模式的区别:装饰器模式为了增强功能,而代理模式是为了加以控制。
行为型
- 平级类关联
- 父类与子类关联
责任链模式
为请求创建了一个接受者对象的链,解耦请求的发送者和接收者
每个接收者包含下一个接收者对象引用
应用案例:冒泡事件、encoding的处理、拦截器、filter过滤、记录器
一层层过滤处理事件,可能多层接收者处理该事件,也可能只有最后一个接收者处理,也可能都不处理
命令模式
行为请求和行为执行解耦
比如GUI中每个按钮都是一个命令操作,记录、撤销、重做等
调用者、执行者、命令 三个类
命令对象要求知道执行者对象
调用者下达命令
revicer = new Reciver(); cmd = new Command(reciver); invoker = new Invoker(); invoker.action(cmd);
解释器模式
常用场景:;编译器,运算表达等可利用场景较少
迭代器模式
顺序访问集合对象的元素,不需要知道集合对象的底层表示
常用于遍历一个聚合对象
中介者模式
该中介者类通常处理不同类之间的通信,支持松耦合。
用一个中介对象封装一些系列对象的交互,使得这一些列的对象不需要显示的相互引用,从而使其耦合松散,将一对多转换成一对一,也易于维护、扩展
使用前提:类的职责清楚不混乱
应用场景:打牌输赢计算(需要一个中介者专门计算输赢情况及金额增减)、机场调度、MVC框架(C为中介者)
中介者容易臃肿
中介者与代理模式的区别:
-
中介者模式:是代理多个对象之间的交互(行为型)
-
代理模式:用一个类去代理另一个类 (结构型)
备忘录模式
保存一个对象的某个状态,便于在适当时候恢复对象
场景:用于用户后悔恢复状态,后悔药、游戏存档、撤销、浏览器后退、数据库事务回滚
原始类、备忘类、备忘管理类三个类
观察者模式
当一个对象状态改变时,它的所有依赖对象将得到通知,并自动更新
注:依赖尽量单一,不复杂,不循环依赖,尽量采用异步模式
虽然叫观察者模式,但实际上还是主动通知观察者,并不是通过第三方异步消息式地去通知观察者,更像订阅模式的主动推送
状态模式
类的行为是基于它的状态改变的
场景:运动员有正常状态、不正常状态、超常状态;服务有开启、等待、关闭状态
如果不是完全围绕状态展开的产品,不建议采用状态模式处理问题,在平常的产品类中,这些状态并不多或复杂,完全可以放入产品类中,不需要细分状态类
状态模式对开闭原则支持不好,结构和实现会随着项目的进度越来越复杂
策略模式
定义一系列的算法,把它们一个个封装起来,并且使它们可相互替换,解决多种相似算法的情况下,使用if…else带来的复杂和难以维护
场景:选择出游方式等系统内许多策略方式类,它们之间的区别仅在于它们的行为,使用策略模式可以动态地让一个对象在许多行为中选择一种行为策略
了解策略模式后,我们清楚该模式是对原综合策略类中方法再次分解抽象模块化的结果,如果系统中的行为策略不多,且可以预见的后续系统不会出现更多的行为策略,那就不必要去单独使用策略模式,避免过度抽象模块化,避免信息暴露太多
模板模式
这是一个很容易理解,并常用的设计模式
定义一个操作或算法的骨架,而将一些步骤延迟到子类中。子类可以不改变操作或算法结构即可重定义算法的某些特殊步骤
一个抽象类公开定义执行它的方法的方式/模板,它的子类可以按需重写方法实现
场景:不同渠道的用户、支付、广告、分享、统计统一实现,造房子(卧室、厨房、卫生间、大厅、天花板等),球类运动,棋等
封装不变的部分,灵活可拓展可变部分
访问者模式
访问者模式,执行操作方法/算法可以随着访问者的改变而改变
不同的访问者,相同的方法,具有不同的行为与结果
策略模式:主动选择及执行策略
访问者模式:根据访问者不同,执行不同算法(访问者的方法)
vistor = new Vistor();
subject = new Subject();
subject.accept(vistor);
public class Subject{
...
public function accept(vistor){
vistor.visit();
}
...
}
其他
MVC模式
M:model,模型代表一个数据存储对象,甚至数据逻辑更新控制器。随着技术的发展,模型不再是简单的数据存储对象,还可以再细分业务逻辑层、数据存储层、数据缓存层等
V:view,视图代表模型包含的数据的可视化
C:controller,控制器控制数据流向模型,并把模型处理好的数据传达给视图,及时更新视图,在中介者模式中,我们说MVC实际上也可以是中介者模式,控制器C是模型M和视图V的中介对象,控制器C对M和V解耦