设计模式
2025-03-26 22:55:14 0 举报
AI智能生成
设计模式
作者其他创作
大纲/内容
设计原则
单一职责原则
就一个类而言,应该仅有一个引起它变化的原因
若想到多余的动机去改变一个类的话,那么就应该考虑类的职责分离
职责过多,会导致职责耦合,一个职责的变化会引发其他职责的变化,导致意外的破坏
核心:职责分离
开放封闭原则
软件实体(类,函数,模块等),应该可以扩展,但是不可以修改
面对需求的变化,对程序的改动是通过增加新代码,而不是修改现有代码
依赖倒转原则
1.高层模块不应该依赖于低层模块,两者都依赖于抽象
2.抽象不应该依赖于细节,细节应该依赖于抽象
针对接口编程,不应该针对实现编程
里氏代换原则
子类型必须能够替换掉它们的父类型
子类型替换父类型,正真复用父类,子类扩展新行为
合成聚合复用原则
通过关联关系,包括组合和聚合关系,来使用一些已有的对象,使之成为新对象的一部分,从而扩展新功能
推荐多使用关联关系,以减少类继承的复杂度
合成
强拥有关系
聚合
弱拥有关系
迪米特法则
如果两个类不必直接通信,那么这两个类就不应该发生直接的相互作用,如果一个类需要调用另一个类的方法时,可以通过第三者转发这个调用
强调类之间的耦合,类应当尽量降低成员的的访问权限,信息的隐藏促进了软件的复用
接口隔离原则
总结
六大设计原则需要灵活应用,遵守程度只要在一个合理的范围内,就算是良好的设计
六大设计原则的度
如果对这项原则遵守的合理的话,这个点应该落在红色的同心圆内部;如果遵守的差,点将会在小圆内部;如果过度遵守,点将会落在大圆外部。
设计分析
良好的设计
良好的设计图
基本可以接受的设计
设计有不足
设计严重不足
设计严重不足
设计过度
设计过度
23种设计模式之结构型模式
结构型模式
适配器模式
简介
将一个类的接口转换成客户希望的另一个接口, 使得原本由于接口不兼容不能再一起工作的类,可以在一起工作
适用场景
复用环境与接口不符:系统要复用现有的类,现有类的接口不符合系统的接口
两个类功能类似,但是接口不同
双方都不太容易修改:第三方组件组件的接口,与系统接口不符
角色
Target: 客户期待的接口,可以是抽象类或者接口
Adapter: 在内部包装一个Adeptee对象,把原接口转为目标接口
Adeptee: 需要适配的类
优缺点
优点
通过引入适配器,可以复用现有的类,而不需要修改源代码,将目标类和适配者解耦合,解决了接口和复用环境不一致的情况
UML
适配器模式UML
桥接模式
简介
定义:将抽象部分与实现部分分离,使它们都可以独立的变化
解决如何应对对象“多维度的变化”问题?
继承方式
继承会导致庞大复杂的继承体系
通过对象组合的方式,Bridge模式把两个角色之间的继承关系改为了耦合的关系,从而使这两者可以从容自若的各自独立的变化,这也是Bridge模式的本意
角色
Client 调用端
抽象类(Abstraction)桥接类
Refined Abstraction 桥接类的子类
Implementor 行为实现类接口(Abstraction接口定义了基于Implementor接口的更高层次的操作)
ConcreteImplementor Implementor的子类
UML
桥接模式
适用场景
如果一个系统需要在构件的抽象化角色和具体化角色之间增加更多的灵活性,避免在两个层次之间建立静态的继承联系,通过桥接模式可以使它们在抽象层建立一个关联关系。
抽象化角色和实现化角色可以以继承的方式独立扩展而互不影响,在程序运行时可以动态将一个抽象化子类的对象和一个实现化子类的对象进行组合,即系统需要对抽象化角色和实现化角色进行动态耦合。
一个类存在两个独立变化的维度,且这两个维度都需要进行扩展。
虽然在系统中使用继承是没有问题的,但是由于抽象化角色和具体化角色需要独立变化,设计要求需要独立管理这两者。
对于那些不希望使用继承或因为多层次继承导致系统类的个数急剧增加的系统,桥接模式尤为适用
优缺点
优点:抽象与实现的分离
实现了抽象和实现部分的分离
更好的可扩展性
可动态的切换实现
实现细节对客户端透明,可以对用户隐藏实现细节
缺点
桥接模式的引入增加了系统的理解和设计难度,针对抽象设计和编程
识别独立变化的维度对于使用范围有一定的局限性
总结
桥接模式关注的是不同的变化维度不同时独立变化,需要将抽象和实现分离
可以参考博客:m/lixiuyu/p/5923160.html
组合模式
简介
组合模式定义了如何将容器对象和叶子对象进行递归组合,使得客户在使用的过程中无须进行区分,可以对他们进行一致的处理。
组合模式组合多个对象形成树形结构以表示“整体-部分”的结构层次。
递归结构来进行组织的文件系统,非常适合使用组合模式
组合模式最重要的点:叶子对象和组合对象实现相同的接口
角色
Component 组合中的对象声明接口
Leaf 叶子对象,叶子结点没有子结点
Composite 容器对象,定义有枝节点行为,用来存储子部件,在Component接口中实现与子部件有关操作
子主题 4
适用场景
需要表示一个对象整体或部分层次,在具有整体和部分的层次结构中,希望通过一种方式忽略整体与部分的差异,可以一致地对待它们。
让客户能够忽略不同对象层次的变化,客户端可以针对抽象构件编程,无须关心对象层次结构的细节
优缺点
优点
可以清楚地定义分层次的复杂对象,表示对象的全部或部分层次,使得增加新构件也更容易
客户端调用简单,客户端可以一致的使用组合结构或其中单个对象
定义了包含叶子对象和容器对象的类层次结构,叶子对象可以被组合成更复杂的容器对象,而这个容器对象又可以被组合,这样不断递归下去,可以形成复杂的树形结构。
更容易在组合体内加入对象构件,客户端不必因为加入了新的对象构件而更改原有代码
缺点
使设计变得更加抽象,对象的业务规则如果很复杂,则实现组合模式具有很大挑战性,而且不是所有的方法都与叶子对象子类都有关联
UML
组合模式UML
总结
组合模式用于将多个对象组合成树形结构以表示“整体-部分”的结构层次。组合模式对单个对象(叶子对象)和组合对象(容器对象)的使用具有一致性。
组合对象的关键在于它定义了一个抽象构建类,它既可表示叶子对象,也可表示容器对象,客户仅仅需要针对这个抽象构建进行编程,无须知道他是叶子对象还是容器对象,都是一致对待
组合模式虽然能够非常好地处理层次结构,也使得客户端程序变得简单,但是它也使得设计变得更加抽象,而且也很难对容器中的构件类型进行限制,这会导致在增加新的构件时会产生一些问题。
例子:Windows文件系统,文件和文件夹
https://www.cnblogs.com/chenssy/p/3299719.html
装饰模式
简介
动态地给一个对象添加一些额外的职责
就增加功能来说,装饰模式比生成子类更为灵活
角色
Component(抽象构件角色):它是具体构件和抽象装饰类的共同父类,以规范准备接受附加责任的对象
ConcreteComponent(具体构件):抽象构件角色的子类(或实现),具体的组件对象,装饰器可以给它增加额外的职责
Docorator(装饰器)
也是抽象构件角色的子类,持有一个抽象构件角色的引用①,通过该引用可以调用装饰之前构件对象的方法,并通过其子类扩展该方法,以达到装饰的目的
ConcreteDecorator(具体装饰类)
具体的装饰类,实现为需要装饰的构件添加新的职责
UML
装饰器模式UML
优缺点
优点
比继承更灵活
可以运行期动态组合的功能
更容易复用功能
有利于装饰器功能的复用,可以给一个对象多次增加同一个装饰器,也可以用同一个装饰器来装饰不同的对象
简化高层定义
装饰模式可以通过组合装饰器方式给对象增添任意多的功能,因此在进行高层定义的时候,只需要定义最基本的功能就可以了,需要的时候结合相应装饰器完成需要的功能
缺点
可能会产生很多细粒度的对象
适用场景
允许向一个现有的对象添加新的功能,同时又不改变其结构。它能让我们在扩展类的时候让系统较好的保持灵活性。
总结
使用对象的组合来代替对象的庞杂的继承
装饰器模式的本质:动态组合
关键代码
public abstract class CondimentDecorator extends Beverage{
@Override
public abstract String getDescription();
}
public class Sugar extends CondimentDecorator{
Beverage beverage ;
public Sugar(Beverage beverage) {
this.beverage = beverage;
}
外观模式
简介
外观模式是一种使用频率非常高的结构型设计模式
为子系统中的一组接口提供一个统一的入口。外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用
角色
Facade(外观角色)
外观类知道哪些子系统类负责处理请求,将客户的请求代理给恰当的子系统对象
SubSystem(子系统角色)
子系统类集合实现了子系统的功能,处理外观类对象指派的任务
UML
外观模式
适用场景
需要将设计进行分层时考虑Facade模式
在开发阶段,子系统往往因为重构变得越来越复杂,增加外观模式可以提供一个简单的接口,减少它们之间的依赖
在维护一个遗留的大型系统时,可以这个系统已经非常难以维护和扩展,可以为新系统开发一个Facade类,来提供设计粗糙或高度复杂的遗留代码的比较清晰简单的接口,让新系统与Facade对象交互,Facade与遗留代码交互所有复杂的工作
优缺点
优点
Facade模式降低了客户端对子系统使用的复杂性。
外观模式松散了客户端与子系统的耦合关系,让子系统内部的模块能更容易扩展和维护
通过合理使用Facade,可以帮助我们更好的划分访问的层次
缺点
过多的或者是不太合理的Facade也容易让人迷惑,到底是调用Facade好呢,还是直接调用模块好
总结
为复杂的子系统提供一个简单的接口
亨元模式
简介
通过共享技术有效地实现了大量细粒度对象的复用
内部状态 vs. 外部状态
内部状态是存储在享元对象内部,一般在构造时确定或通过setter设置,并且不会随环境改变而改变的状态,因此内部状态可以共享
外部状态是随环境改变而改变、不可以共享的状态。外部状态在需要使用时通过客户端传入享元对象。外部状态必须由客户端保存。
角色
FlyWeight 享元接口或者(抽象享元类),定义共享接口
ConcreteFlyWeight 具体享元类,该类实例将实现共享
UnSharedConcreteFlyWeight 非共享享元实现类
FlyWeightFactory 享元工厂类,控制实例的创建和共享
UMl
享元模式
优缺点
优点
享元模式的外部状态相对独立,使得对象可以在不同的环境中被复用(共享对象可以适应不同的外部环境)
享元模式可共享相同或相似的细粒度对象,从而减少了内存消耗,同时降低了对象创建与垃圾回收的开销
缺点
外部状态由客户端保存,共享对象读取外部状态的开销可能比较大
享元模式要求将内部状态与外部状态分离,这使得程序的逻辑复杂化,同时也增加了状态维护成本
适用场景
面向对象技术可以很好的解决一些灵活性或可扩展性问题,但在很多情况下需要在系统中增加类和对象的个数。当对象数量太多时,将导致对象创建及垃圾回收的代价过高,造成性能下降等问题。享元模式通过共享相同或者相似的细粒度对象解决了这一类问题
总结
享元模式中,最关键的享元工厂。它将维护已创建的享元实例,并通过实例标记(一般用内部状态)去索引对应的实例。当目标对象未创建时,享元工厂负责创建实例并将其加入标记-对象映射。当目标对象已创建时,享元工厂直接返回已有实例,实现对象的复用
复制重用
代理模式
简介
为其他对象提供一种代理以便控制对这个对象的访问
可以详细控制访问某个类(对象)的方法,在调用这个方法前作的前置处理(统一的流程代码放到代理中处理)。调用这个方法后做后置处理。
例子:明星的经纪人,租房的中介等等都是代理
代理模式分类
静态代理
静态定义代理类,我们自己静态定义的代理类
动态代理(较为特殊)
通过程序动态生成代理类,该代理类不是我们自己定义的。而是由程序自动生成
JDK自带的动态代理
javaassist字节码操作库实现
CGLIB
ASM(底层使用指令,可维护性较差)
远程(Remote)代理
虚拟(Virtual)代理
保护(Protector Access)代理
智能引用(SmartReference)代理
动态(DynamicProxy)代理
UML
代理模式
UML图
简化理解图
角色
抽象角色
指代理角色(经纪人)和真实角色(明星)对外提供的公共方法
真实角色
需要实现抽象角色接口,定义了真实角色所要实现的业务逻辑,以便供代理角色调用,真正的业务逻辑在此
代理角色
需要实现抽象角色接口,是真实角色的代理,通过真实角色的业务逻辑方法来实现抽象方法,并可以附加自己的操作, 将统一的流程控制都放到代理角色中处理!
适用场景
当客户端对象需要访问远程主机中的对象时可以使用远程代理
当需要为一个对象的访问(引用)提供一些额外的操作时可以使用智能引用代理
当需要控制对一个对象的访问,为不同用户提供不同级别的访问权限时可以使用保护代理
当需要用一个消耗资源较少的对象来代表一个消耗资源较多的对象,从而降低系统开销、缩短运行时间时可以使用虚拟代理
当需要为某一个被频繁访问的操作结果提供一个临时存储空间,以供多个客户端共享访问这些结果时可以使用缓冲代理。通过使用缓冲代理,系统无须在客户端每一次访问时都重新执行操作,只需直接从临时缓冲区获取操作结果即可
优缺点
由于在客户端和真实主题之间增加了代理对象,因此有些类型的代理模式可能会造成请求的处理速度变慢
实现代理模式需要额外的工作,而且有些代理模式的实现过程较为复杂
总结
关键在于理解代理角色
23种设计模式之行为型模式
行为型模式
观察者模式
简介
观察者模式(又被称为发布-订阅(Publish/Subscribe)模式
它定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态变化时,会通知所有的观察者对象,使他们能够自动更新自己。
多个监听一个,一个变化,通知监听的对象都变化
角色
Subject:抽象主题(抽象被观察者)
抽象主题角色把所有观察者对象保存在一个集合里,每个主题都可以有任意数量的观察者,抽象主题提供一个接口,可以增加和删除观察者对象
ConcreteSubject:具体主题(具体被观察者)
该角色将有关状态存入具体观察者对象,在具体主题的内部状态发生改变时,给所有注册过的观察者发送通知。
Observer:抽象观察者,是观察者者的抽象类
它定义了一个更新接口,使得在得到主题更改通知时更新自己。
ConcrereObserver:具体观察者
实现抽象观察者定义的更新接口,以便在得到主题更改通知时更新自身的状态。
UML
子主题 1
实例
多个的微信用户关注了程序猿这个公众号,当这个公众号更新时就会通知这些订阅的微信用户
适用场景
关联行为场景,需要注意的是,关联行为是可拆分的,而不是“组合”关系
事件多级触发场景
跨系统的消息交换场景,如消息队列、事件总线的处理机制
优缺点
优点
解除耦合,让耦合的双方都依赖于抽象,从而使得各自的变换都不会影响另一边的变换
缺点
在应用观察者模式时需要考虑一下开发效率和运行效率的问题,程序中包括一个被观察者、多个观察者,开发、调试等内容会比较复杂,而且在Java中消息的通知一般是顺序执行,那么一个观察者卡顿,会影响整体的执行效率,在这种情况下,一般会采用异步实现
总结
子主题 1
模板方法模式
简介
定义一个操作中的算法的骨架,而将步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义算法的某些特定步骤。
角色
抽象类(AbstractClass):实现了模板方法,定义了算法的骨架
具体类(ConcreteClass):实现抽象类中的抽象方法,已完成完整的算法
UMl
子主题 1
适用场景
在某些类的算法中,用了相同的方法,造成代码的重复
控制子类扩展,子类必须遵守算法规则
优缺点
优点
模板方法模式通过把不变的行为搬移到超类,去除了子类中的重复代码
子类实现算法的某些细节,有助于算法的扩展
通过一个父类调用子类实现的操作,通过子类扩展增加新的行为,符合“开放-封闭原则”。
缺点
每个不同的实现都需要定义一个子类,这会导致类的个数的增加,设计更加抽象
例子:https://www.cnblogs.com/wangjq/archive/2012/07/09/2582978.html
命令模式
简介
对命令进行封装,将发出命令与执行命令的责任分开
每一个命令都是一个操作:请求的一方发出请求,要求执行一个操作;接收的一方收到请求,并执行操作。
请求方和接收方独立开来,使得请求的一方不必知道接收请求的一方的接口,更不必知道请求是怎么被接收,以及操作是否被执行、何时被执行,以及是怎么被执行的
使请求本身成为一个对象,这个对象和其它对象一样可以被存储和传递。
命令模式的关键在于引入了抽象命令接口,且发送者针对抽象命令接口编程,只有实现了抽象命令接口的具体命令才能与接收者相关联
适用场景
当需要对行为进行“记录、撤销/重做”等处理时
系统需要将请求者和接收者解耦,使得调用者和接收者不直接交互
系统需要在不同时间指定请求、请求排队和执行请求
系统需要将一组操作组合在一起,即支持宏命令
角色
抽象命令(Command):定义命令的接口,声明执行的方法
具体命令(ConcreteCommand):具体命令,实现要执行的方法,它通常是“虚”的实现;通常会有接收者,并调用接收者的功能来完成命令要执行的操作
接收者(Receiver):真正执行命令的对象。任何类都可能成为一个接收者,只要能实现命令要求实现的相应功能
调用者(Invoker):要求命令对象执行请求,通常会持有命令对象,可以持有很多的命令对象。这个是客户端真正触发命令并要求命令执行相应操作的地方,也就是说相当于使用命令对象的入口
客户端(Client):命令由客户端来创建,并设置命令的接收者
UML
优缺点
优点
解除了请求者与实现者之间的耦合,降低了系统的耦合度。
对请求排队或记录请求日志,支持撤销操作
可以容易地设计一个组合命令
新命令可以容易地加入到系统中
缺点
因为针对每一个命令都需要设计一个具体命令类,使用命令模式可能会导致系统有过多的具体命令类
总结:命令模式就是将命令和执行命令者解耦
状态模式
简介
当一个对象的内在状态改变时允许改变其行为,这个对象看起来像是改变了其类
主要解决的是当控制一个对象状态转换的条件表达式过于复杂时的情况。把状态的判断逻辑转移到表示不同的一系列类当中,可以把复杂的逻辑判断简单化。
角色
上下文环境(Context):它定义了客户程序需要的接口并维护一个具体状态角色的实例,将与状态相关的操作委托给当前的Concrete State对象来处理。
抽象状态(State):定义一个接口以封装使用上下文环境的的一个特定状态相关的行为
具体状态(Concrete State):实现抽象状态定义的接口
UML
状态模式
优缺点
优点
状态模式将与特定状态相关的行为局部化,并且将不同状态的行为分割开来
所有状态相关的代码都存在于某个ConcereteState中,所以通过定义新的子类很容易地增加新的状态和转换。
状态模式通过把各种状态转移逻辑分不到State的子类之间,来减少相互间的依赖
缺点
导致较多的ConcreteState子类
适用场景
当一个对象的行为取决于它的状态,并且它必须在运行时刻根据状态改变它的行为时,就可以考虑使用状态模式来。
一个操作中含有庞大的分支结构,并且这些分支决定于对象的状态
例子:电灯有两个状态,开(亮)与关(不亮)
总结
根据状态改变行为
解释器模式
解释器模式的定义是一种按照规定语法进行解析的方案,在现在项目中使用的比较少
给定一门语言,定义它的文法的一种表示,并定义一个解释器,该解释器使用该表达式来解释语言中的句子
尽量不要再重要的模块中使用解释器模式,否则维护会一个很大的问题。在项目中可以使用shell,jRuby,Groovy
等脚本语言来代替解释器模式,弥补Java编译型语言的不足
总结:解释器模式使用解释语法,使用的比较少
职责链模式
简介
将能够处理同一类请求的对象连成一条链所提交的请求沿着链传递,链上的对象逐个判断是否有能力处理该请求,如果能则处理,如果不能则传递给链上的下一个对象
链表和非链表两种实现,但是使用集合和数组等非链表实现在实际开发中更方便更常用。
责任链模式的重点在“链”上,有一条链去处理相似的请求在链中决定由谁去处理这个请求,并返回相应的结果
角色
抽象处理者角色(Handler):它定义了一个处理请求的接口。当然对于链子的不同实现,也可以在这个角色中实现后继链。
具体处理者角色(Concrete Handler):实现抽象角色中定义的接口,并处理它所负责的请求。如果不能处理则访问它的后继者。
适用场景
Java中的异常机制就是一种责任链模式,catch链就是一种责任链
有多个的对象可以处理一个请求,哪个对象处理该请求运行时刻自动确定
在不明确指定接收者的情况下,向多个对象中的一个提交一个请求
处理一个请求的对象集合应被动态指定
优缺点
优点
将请求者和发送者解耦
简化对象的处理过程
可以通过改变链中的成员或成员的顺序来动态的新增或则删除责任
缺点
不能保证请求一定会执行,可能或落到责任链之外
不容易观察运行时的特征,处理请求的方法比较分散
总结
参考:https://www.cnblogs.com/java-my-life/archive/2012/05/28/2516865.html
中介者模式
简介
用一个中介对象来封装一系列的对象交互。中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。
角色
Mediator:中介者定义一个接口用于与各同事(Colleague)对象通信
ConcreteMediator:具体中介者通过协调各同事对象实现协作行为,了解并维护它的各个同事
Colleague:抽象同事类
Colleagueclass:具体同事类。每个具体同事类都只需要知道自己的行为即可,但是他们都需要认识中介者
适用场景
一组对象以定义良好但是复杂的方式进行通信,产生的相互依赖关系结构混乱且难以理解
一个对象引用其他很多对象并且直接与这些对象通信,导致难以复用该对象
想定制一个分布在多个类中的行为,但又不想生成太多的子类
优缺点
优点
简化了对象之间的关系,将系统的各个对象之间的相互关系进行封装,将各个同事类解耦,使得系统变为松耦合
提供系统的灵活性,使得各个同事对象独立而易于复用
缺点
中介者模式中,中介者角色承担了较多的责任,所以一旦这个中介者对象出现了问题,整个系统将会受到重大的影响
新增加一个同事类时,不得不去修改抽象中介者类和具体中介者类,此时可以使用观察者模式和状态模式来解决这个问题。
UML
中介者模式
总结
中介者模式,定义了一个中介对象来封装系列对象之间的交互。中介者使各个对象不需要显式地相互引用,从而使其耦合性降低,而且可以独立地改变它们之间的交互。中介者模式一般应用于一组定义良好的对象之间需要进行通信的场合以及想定制一个分布在多个类中的行为,而又不想生成太多的子类的情形下。
类似于一个交互中心
访问者模式
简介
对于某个对象或者一组对象,不同的访问者,产生的结果不同,执行操作也不同
角色
抽象访问者(Visitor)角色:定义接口,声明一个或多个访问操作。
具体访问者(ConcreteVisitor)角色:实现抽象访问者所声明的接口,也就是抽象访问者所声明的各个访问操作。
抽象元素(Visitable)角色:声明一个接受操作,接受一个访问者对象作为一个参数
具体元素结点(ConcreteElement)角色:实现抽象结点所规定的接受操作
数据结构对象(ObjectStructure)角色:可以遍历结构中的所有元素,提供一个接口让访问者对象都可以访问每一个元素。
UML
子主题 1
适用场景
如果想对一个对象结构,实施一些依赖于对象结构中的具体类的操作,可以使用访问者模式
如果想对一个对象结构中的各个元素,进行很多不同的而且不相关的操作,为了避免这些操作使得类变得杂乱,可以使用访问者模式,把这些操作分散到不同的访问者对象中去,每个访问者对象实现同一类功能
如果对象结构很少变动,但是需要经常给对象结构中的元素对象定义新的操作,可以使用访问者模式
参考:http://alaric.iteye.com/blog/1942517
策略模式
简介
定义一系列的算法,把它们一个个封装起来, 并且使它们可相互替换
创建表示各种策略的对象和一个行为随着策略对象改变而改变的 context 对象
使用场景
1、如果在一个系统里面有许多类,它们之间的区别仅在于它们的行为,那么使用策略模式可以动态地让一个对象在许多行为中选择一种行为。
2、一个系统需要动态地在几种算法中选择一种。
3、如果一个对象有很多的行为,如果不用恰当的模式,这些行为就只好使用多重的条件选择语句来实现。
优缺点
优点
算法可以自由切换
避免使用多重条件判断
扩展性良好
缺点
策略类会增多
所有策略类都需要对外暴露
总结
注意事项:如果一个系统的策略多于四个,就需要考虑使用混合模式,解决策略类膨胀的问题。
备忘录模式
简介
备忘录模式(Memento Pattern)保存一个对象的某个状态,以便在适当的时候恢复对象。
在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态
使用场景
需要保存/恢复数据的相关状态场景。
提供一个可回滚的操作
优缺点
优点
1、给用户提供了一种可以恢复状态的机制,可以使用户能够比较方便地回到某个历史的状态。
2、实现了信息的封装,使得用户不需要关心状态的保存细节。
缺点
消耗资源。如果类的成员变量过多,势必会占用比较大的资源,而且每一次保存都会消耗一定的内存
注意事项
1、为了符合迪米特原则,还要增加一个管理备忘录的类。
2、为了节约内存,可使用原型模式+备忘录模式。
参考:http://www.runoob.com/design-pattern/memento-pattern.html
迭代器模式
简介
用于顺序访问集合对象的元素,不需要知道集合对象的底层表示。
提供一种方法顺序访问一个聚合对象中各个元素, 而又无须暴露该对象的内部表示
Java 和 .Net 编程环境中非常常用的设计模式
适用场景
1、访问一个聚合对象的内容而无须暴露它的内部表示
2、需要为聚合对象提供多种遍历方式
3、为遍历不同的聚合结构提供一个统一的接口。
优缺点
优点
1、它支持以不同的方式遍历一个聚合对象。
2、迭代器简化了聚合类。
3、在同一个聚合上可以有多个遍历。
4、在迭代器模式中,增加新的聚合类和迭代器类都很方便,无须修改原有代码。
缺点
由于迭代器模式将存储数据和遍历数据的职责分离,增加新的聚合类需要对应增加新的迭代器类,类的个数成对增加,这在一定程度上增加了系统的复杂性。
总结
迭代器模式就是分离了集合对象的遍历行为,抽象出一个迭代器类来负责,这样既可以做到不暴露集合的内部结构,又可让外部代码透明地访问集合内部的数据。
设计模式详解
UML
继承
分支主题
代码表现:泛化关系表现为继承非抽象类
实现
分支主题
代码表现:实现关系表现为继承抽象类
组合
分支主题
一种强依赖的特殊聚合关系,如果整体不存在了,则部分也不存在了
代码表现:部分由整体在初始化时创建
聚合
分支主题
整体和部分不是强依赖的,即使整体不存在了,部分仍然存在
代码表现:部分set到整体
关联
分支主题
代码表现:关联对象通常是以成员变量的形式实现的
依赖
分支主题
代码表现:类构造方法及类方法的传入参数,箭头的指向为调用关系
行为型模式
中介模式
分支主题
适用场景
使用中介者模式可以将对象间一对多的关联转变为一对一的关联,使对象间的关系易于理解和维护
应用
Controller 作为一种中介者,它负责控制视图对象View和模型对象Model之间的交互
命令模式
分支主题
适用场景
系统需要将请求调用者和请求接收者解耦,使得调用者和接收者不直接交互
应用
遥控器-电视
观察者模式
分支主题
适用场景
一个对象的改变将导致其他一个或多个对象也发生改变
应用
kafka
某团队战斗游戏中某队友牺牲将给所有成员提示等等
状态模式
分支主题
适用场景
这些状态之间可以发生转换时使用状态模式
代码中包含大量与对象状态有关的条件语句
应用
工作流
策略模式
http://design-patterns.readthedocs.io/zh_CN/latest/_images/Strategy.jpg
适用场景
某个类的某一行为存在多种实现方式
动态地让一个对象在许多行为中选择一种行为
应用
责任链模式
分支主题
适用场景
有多个对象可以处理同一个请求,具体哪个对象处理该请求由运行时刻自动确定
应用
请假审批
备忘录模式
分支主题
适用模式
保存一个对象在某一个时刻的状态或部分状态
应用
挑战BOSS之前,保存
结构型模式
适配模式
分支主题
适用环境
系统需要使用现有的类,而这些类的接口不符合系统的需要
应用
抽象的JDBC接口和各个数据库引擎API之间都需要相应的适配器软件
代理模式
分支主题
适用环境
远程(Remote)代理:为一个位于不同的地址空间的对象提供一个本地 的代理对象
虚拟(Virtual)代理:如果需要创建一个资源消耗较大的对象,先创建一个消耗相对较小的对象来表示,真实对象只在需要时才会被真正创建
缓冲(Cache)代理:为某一个目标操作的结果提供临时的存储空间,以便多个客户端可以共享这些结果
应用
RPC框架
设置缓存
装饰模式
分支主题
适用环境
需要动态地给一个对象增加功能,这些功能也可以动态地被撤销
应用
输入、输出流
外观模式
分支主题
适用环境
要为一个复杂子系统提供一个简单接口时可以使用外观模式
系统中每一层的入口,层与层之间不直接产生联系
应用
springMVC模式
享元模式
分支主题
适用场景
一个系统有大量相同或者相似的对象,由于这类对象的大量使用
应用
String=“ABCD”
组合模式
分支主题
适用场景
一个对象整体或部分层次,在具有整体和部分的层次结构中,希望通过一种方式忽略整体与部分的差异,可以一致地对待它们。
应用
树型结构
创建型模式
简单工厂模式
分支主题
适用环境
工厂类负责创建的对象比较少
客户端只知道传入工厂类的参数,对于如何创建对象不关心
应用
KeyGenerator keyGen=KeyGenerator.getInstance('DESede');
Cipher cp=Cipher.getInstance('DESede');
工厂方法模式
分支主题
与简单工厂相比,用工厂实体类代替了参数
抽象工厂模式
分支主题
适用环境
系统中有多于一个的产品族,而每次只使用其中某一产品族
应用
在很多软件系统中需要更换界面主题,要求界面中的按钮、文本框、背景色等一起发生改变时
建造者模式
分支主题
适用环境
创建复杂对象
部分与主体之间紧密结合,不可分离
应用
人物角色包括人体、服装、装备等组成部分
单例模式
分支主题
适用环境
一是某个类只能有一个实例
应用
主键编号生成器
23种设计模式之创建型模式
创建型模式
简单工厂模式
又称静态工厂方法(Static Factory Method)模式
简单工厂模式是由一个工厂对象决定创建出哪一种产品类的实例
简单工厂的优点/缺点
优点
产品使用者不需要关心产品类的创建过程,与具体产品的实现类达到解耦的效果
缺点
新增一个产品类的时候,需要修改原先的工厂方法
适用场景
当工厂类负责创建的对象比较少的时候
角色
抽象产品
具体产品-继承抽象产品
具体工厂-生产不同的产品,返回抽象产品
产品使用者-依赖具体工厂生产的不同产品
UML
简单工厂模式UML
总结
使用静态的工厂方法解除产品使用者与具体产品之间的强耦合关系
工厂方法模式(Factory Method)
又称多态性工厂模式
解决问题
解决的仍然是软件设计中与创建对象有关的问题。它可以更好的处理客户的需求变化
工厂方法模式的核心的工厂类不再负责所有的产品的创建,而是将具体创建的工作交给子类去做。该核心类成为一个抽象工厂角色,仅负责给出具体工厂子类必须实现的接口,而不接触哪一个产品类应当被实例化这种细节。
工厂方法的优点/缺点
优点
克服了简单工厂模式违背开闭原则,又保持了封装对象创建过程的优点
缺点
每增加一个产品,就需要加一个产品工厂类,增加了额外的开发量
角色
抽象产品:具体产品的公共接口
具体产品:描述生产的具体产品
抽象工厂:具体工厂的公共接口
具体工厂:实现FactoryMethod工厂方法创建产品的实例
UML
子主题 1
适用场景
一个类想要由自己的子类来定义某对象的创建过程。
单例模式
保证一个类只有一个实例,并提供一个访问它的全局访问点
单例模式分类
饿汉式
类加载的时候就立即创建对象
懒汉式
在外部对象第一次请求实例的时候才去创建
注意线程安全的问题
解决方法:双重检查模式(DCL)
Public static Singleton getInstance()
{
If(singleton == null)
{
Synchronized(singleton.class)
{
If(singleton == null)
{
Singleton = new Singleton();
}
}
}
}
单例模式要素
私有构造方法
Private Singleton(){}
私有静态引用指向自己实例
Private static final Singleton singleton ;
以自己实例为返回值的公有静态方法
Public static Singleton getInstance(){}
适用场景
单例模式只允许创建一个对象,因此节省内存,加快对象访问速度,因此对象需要被公用的场合适合使用,如多个模块使用同一个数据源连接对象等等
需要频繁实例化然后销毁的对象。
创建对象时耗时过多或者耗资源过多,但又经常用到的对象
有状态的工具类对象
频繁访问数据库或文件的对象
优缺点
优点
单例模式具有一定的伸缩性,类自己来控制实例化进程,类就在改变实例化进程上有相应的伸缩性
避免对共享资源的多重占用
缺点
不适用于变化的对象
滥用单例将带来一些负面问题
由于单利模式中没有抽象层,因此单例类的扩展有很大的困难
注意
使用懒单例模式时注意线程安全问题
饿单例模式和懒单例模式构造方法都是私有的,因而是不能被继承的,有些单例模式可以被继承(如登记式模式)
使用时不能用反射模式创建单例,否则会实例化一个新的对象
建造者模式
建造者返回给客户一个完整的的产品对象,而客户端无须关心该对象所包含的额属性和组建方式,这就是建造者模式的设计动机
定义
建造者模式将一个复杂对象的构建与表示分离,使得同样的构建过程可以创建不同的表示
角色
抽象建造者:它声明为创建一个Product对象的各个部件指定的抽象接口
具体建造者:实现抽象接口,构建和装配各个部件
产品角色:一个具体的产品对象
指挥者:构建一个使用Builder接口的对象。它主要是用于创建一个复杂的对象
隔离了客户与对象的生产过程
负责控制产品对象的生产过程
优缺点
优点
将复杂产品的创建步骤分解在不同的方法中,使得创建过程更加清晰,使得我们能够更加精确的控制复杂对象的产生过程
将产品的创建过程与产品本身分离开来,可以使用相同的创建过程来得到不同的产品。也就说细节依赖抽象
每一个具体建造者都相对独立,而与其他的具体建造者无关,因此可以很方便地替换具体建造者或增加新的具体建造者,用户使用不同的具体建造者即可得到不同的产品对象
缺点
建造者模式所创建的产品一般具有较多的共同点,其组成部分相似,如果产品之间的差异性很大,则不适合使用建造者模式,因此其使用范围受到一定的限制
如果产品的内部变化复杂,可能会导致需要定义很多具体建造者类来实现这种变化,导致系统变得很庞大
适用场景
需要生成的产品对象有复杂的内部结构,这些产品对象通常包含多个成员属性
隔离复杂对象的创建和使用,并使得相同的创建过程可以创建不同的产品
UML
建造者模式UML
参考博客:https://www.cnblogs.com/chenssy/p/3307787.html
总结:按照一定的流程组建复杂的对象,做到很好的解耦,例子:KFC套餐
原型模式
简介
原型模式主要用于对象的复制,它的核心是原型类
原型模式是一种比较简单的模式,也非常容易理解,实现一个接口,重写一个方法即完成了原型模式。在实际应用中,原型模式很少单独出现。经常与其他模式混用,他的原型类Prototype也常用抽象类来替代。
Prototype类
实现Cloneable接口。在java语言有一个Cloneable接口,它的作用只有一个,就是在运行时通知虚拟机可以安全地在实现了此接口的类上使用clone方法。在java虚拟机中,只有实现了这个接口的类才可以被拷贝,否则在运行时会抛出CloneNotSupportedException异常。
重写Object类中的clone方法。Java中,所有类的父类都是Object类,Object类中有一个clone方法,作用是返回对象的一个拷贝,但是其作用域protected类型的,一般的类无法调用,因此,Prototype类需要将clone方法的作用域修改为public类型。
优点
原型模式创建对象比直接new一个对象在性能上要好的多
因为Object类的clone方法是一个本地方法,它直接操作内存中的二进制流,特别是复制大对象时,性能的差别非常明显。
简化对象的创建,创建对象就像复制粘贴一样简单
适用场景
在需要重复地创建相似对象时可以考虑使用原型模式
注意事项
使用原型模式复制对象不会调用类的构造方法,它直接在内存中复制数据,不会调用到类的构造方法。单例模式与原型模式是冲突的,在使用时要特别注意。
注意深拷贝与浅拷贝的问题
浅拷贝
Object类的clone方法只会拷贝对象中的基本的数据类型,对于数组、容器对象、引用对象等都不会拷贝,这就是浅拷贝
深拷贝
必须将原型模式中的数组、容器对象、引用对象等另行拷贝
例子
public class Prototype implements Cloneable {
private ArrayList list = new ArrayList();
public Prototype clone(){
Prototype prototype = null;
try{
prototype = (Prototype)super.clone();
prototype.list = (ArrayList) this.list.clone();
}catch(CloneNotSupportedException e){
e.printStackTrace();
}
return prototype;
}
}
深拷贝和浅拷贝的区别
深拷贝与浅拷贝问题中,会发生深拷贝的有java中的8中基本类型以及他们的封装类型,另外还有String类型。其余的都是浅拷贝。
0 条评论
下一页