Skip to content

zhouchao92/design-pattern

Repository files navigation

GOF(Group of Four)23种设计模式

GOF23种设计模式

创建型模式

  • 单例模式
  • 工厂模式
  • 抽象工厂模式
  • 建造者模式
  • 原型模式
单例模式 Singleton

核心作用:保证一个类只有一个实例,并且提供一个访问该实例的全局访问点。

应用场景
  • Windows任务管理器
  • Windows回收站
  • 网站的计数器
  • 应用程序的日志应用
  • 数据库连接池的设计
  • 操作系统的文件系统
  • Application(Servlet编程)
  • Spring的Bean
  • Servlet
  • Spring MVC框架/Struts框架中控制器对象
常见的五种单例模式
  • 主要:
    • 饿汉式:线程安全,调用效率高,不能延时加载。
    • 懒汉式:线程安全,调用效率不高,可以延时加载。
  • 其他:
    • 双重检测锁式:由于JVM底层内部模型原因,偶尔会出问题,不建议使用。
    • 静态内部类式:线程安全,调用效率高,可以延时加载。
    • 枚举单例:线程安全,调用效率高,不能延时加载,并且可以天然的防止反射和反序列化漏洞。

单例对象性能比较:

  • 占用资源少,不需要延时加载 枚举式 好于 饿汉式
  • 占用资源大,需要延时加载 静态内部类模式 好于 懒汉式

注意:使用反射、反序列化破解单例模式。

工厂模式 Factory

实现了创建者调用者的分离。

核心本质:

  • 实例化对象,用工厂方法代替new操作。

  • 将选择实现类、创建对象同意管理和控制,从而将调用者根实现类解耦。

  • 简单工厂模式 用来生产同一等级结构的任意产品(对于增加新的产品,需要修改已有代码)。

  • (静态工厂模式)工厂类使用静态方法,通过接收的参数的不同来返回不同的对象实例,不完全满足OCP。

  • 工厂方法模式 用来生产同一等级结构的固定产品(支持增加任意产品),不修改已有类的前提下,通过增加新的工厂类实现扩展。

  • 抽象工厂模式 用来生产不同产品族的全部产品(对于增加新的产品,无能为力,支持增加产品族) 抽象工厂模式是工厂方法模式的升级版本,在有多个业务品种、业务分类时,通过抽象工厂模式产生需要的对象是一种非常好的解决方式,不可以增加产品,可以增加产品族。

应用场景
  • JDK中Calendar的getInstance方法
  • JDBC中的Connection对象的获取
  • Hibernate中SessionFactory创建Session
  • Spring中IOC容器创建管理bean对象
  • XML解析时的DocumentBuilderFactory创建解析器对象
  • 反射中Class对象的newInstance()
面向对象设计的基本原则
  • OCP(开闭原则,Open-Closed Principle) 一个软件的实体应当对扩展开放,对修改关闭。
  • DIP(依赖倒转原则,Dependence Inversion Principle) 要针对接口编程,不要针对实现编程。
  • LoD(迪米特法则,Law of Demeter) 只与你喜欢的朋友通信,而避免和陌生人通信。 一个软件实体应当尽可能少的与其他实体发生相互作用
简单工厂模式vs工厂方法模式
  • 结构复杂度:简单工厂模式优于工厂方法模式。
  • 代码复杂度:简单工厂模式在结构方面相对简单,在代码方面比较简单。增加类时需要添加很多方法,而工厂方法只完成单一任务,代码较简单。
  • 客户端编程难度:工厂方法模式在工厂类结构引入了接口从而满足OCP(开闭原则),但在客户端编码中需要对工厂类进行实例化,而简单工厂模式的工厂类是个静态类,在客户端无需实例化。
  • 管理上的难度:工厂方法模式**>**简单工厂模式
建造者模式 Builder

本质:分离了对象组件的单独构造(由Builder来负责)和装配(由Director负责)从而可以构造出复杂的对象 适合于:某个对象的构建过程复杂的情况下使用。 由于实现了构建和装配的解耦。不同的构建器,相同的装配,也可以做出不同的对象;相同的构建器,不同的装配顺序也可以做出不同的对象。也就是实现了构建算法、装配算法的解耦,实现了更好的复用

应用场景
  • StringBuilder类的append()方法
  • SQL中的PreparedStatement
  • JDOM中,DomBuilder、SAXBuilder
原型模式 Prototype

通过new产生一个对象需要非常繁琐的数据准备或访问权限,则可以使用原型模式。

以某个对象为原型,复制出新的对象。

效率高,直接克隆,避免了重新执行构造过程步骤。

克隆类似于new但不同于new,new创建对象属性采用的是默认值,克隆出的对象的属性值完全和原型对象相同,并且克隆出的新对象不会影响原型对象。

实现
  • Cloneable接口和clone()方法。
  • Prototype模式中实现起来最困难的地方就是内存复制操作,Java中提供了clone()方法。

浅克隆 深克隆:修改clone()方法;序列化和反序列化

创建者模式总结

单例模式:保证一个类只有一个实例,并且提供一个访问该实例的全局访问点。

工厂模式:

  • 简单工厂模式:用来生产同一等级结构中的任意产品(对于增加新的产品,需要修改已有代码)
  • 工厂方法模式:用来生产同一等级结构中的固定产品(支持增加任意产品)
  • 抽象工厂模式:用来生产不同产品族的全部产品(对于新增加的产品,无能为力,支持增加产品族)

建造者模式:分离了对象子组件的单独构造(由Builder负责)和装配(由Director负责),从而可以构造出复杂的对象。

原型模式:通过new产生一个对象需要非常繁琐的数据准备或访问权限,则可以使用原型模式。

结构型模式

核心作用:从程序的结构上实现松耦合,从而可以扩大整体的类结构,用来解决更大的问题。

  • 适配器模式
  • 桥接模式
  • 装饰模式
  • 组合模式
  • 外观模式
  • 享元模式
  • 代理模式
适配器模式 Adapter

将一个类的接口转换成客户希望的另一个接口。使得由于接口不兼容而不能一起工作的那些类可以在一起工作。

  • 目标接口Target:客户所期待的接口,目标可以是具体的或抽象的类,也可以是接口。
  • 需要适配的类Adapter:通过包装一个需要适配的对象,把原接口转换成目标接口。
使用场景
  • 旧系统改造和升级
  • 系统维护
  • java.io.InputStreamReader InputStream
  • java.io.OutputStreamWriter OutputStream
代理模式 Proxy Pattern

核心作用:通过代理,控制对对象的访问。

可以详细控制访问某个(某类)对象的方法,在调用这个方法前做前置处理,调用这个方法后做后置处理(AOP的微观实现)。

核心角色
  • 抽象角色:定义代理角色和真实角色的公共对外方法。
  • 真实角色:实现抽象角色,定义真实角色所要实现的业务逻辑,供代理角色调用。关注真正的业务逻辑。
  • 代理角色:实现抽象角色,是真实角色的代理,通过真实角色的业务逻辑方法来实现抽象方法,并可以附加自己的操作。将统一的流程控制放到代理角色中处理。
应用场景
  • 安全代理:屏蔽对真实角色的直接访问。
  • 远程代理:通过代理类处理远程方法调用RMI。
  • 延迟加载:先加载轻量级的代理对象,真正需要在加载真实对象。
分类
  • 静态代理:静态定义代理类
  • 动态代理类:动态生成代理类
    • JDK自带的动态代理 java.lang.reflect.Proxy 动态生成代理类和对象 java.lang.reflect.InvocationHandler 处理器接口,可以通过invoke()方法实现对真实角色的代理访问。 每次通过Proxy生产代理类对象时都要指定对应的处理器对象
    • java assist字节码操作库实现
    • CGLIB
    • ASMI底层使用指令,可维护性较差

动态代理相对于静态代理的优点:抽象角色中(接口)都被转移到调用处理器一个集中的方法中处理,更加灵活、统一地处理众多的方法。

开发框架中的应用场景
  • struts2中拦截器的实现
  • 数据库连接池关闭处理
  • Hibernate中延时加载的实现
  • MyBatis中实现拦截器插件
  • AspectJ的实现
  • Spring中AOP的实现
    • 日志拦截
    • 声明式事务处理
  • Web Service
  • RMI远程方法调用
  • 开源AOP框架AspectJ
桥接模式 Bridge

分层继承结构

核心要点:处理多层继承结构,处理维度变化的场景 。将各个维度设计成独立的继承结构,使各个维度可以独立的扩展在抽象层建立关联

实际开发中应用场景
  • JDBC驱动程序
  • AWT中的Peer架构
  • 银行日志管理
    • 格式分类:操作日志、交易日志、异常日志
    • 距离分类:本地记录日志、异地记录日志
  • 人力资源系统中奖金计算模块
    • 奖金分类:个人奖金、团体奖金、激励奖金
    • 部门分类:人事部门、销售部门、研发部门
  • OA系统中的消息处理
    • 业务类型:普通消息、加急消息、特急消息
    • 发送类型方式:系统内消息、手机短信、邮件
组合模式 Composite

把部分和整体的关系用树形结构来表示,从而使客户端可以使用统一的方式处理部分对象和整体对象。

核心:

  • 抽象构件Component角色 定义了子叶和容器构建的共同点。
  • 叶子Leaf构件角色 无子节点。
  • 容器Composite构件角色 有容器特征,可以包含子节点。

工作流程分析

  1. 将容器和叶子进行递归组合,使得用户在使用时可以一致性地对待容器和叶子。
  2. 当容器对象地指定方法被调用时,将遍历整个树形结构,寻找也包含这个方法的成员,并调用执行。递归
应用场景
  • 操作系统中的资源管理器
  • GUI中的容器层次图
  • XML的文件解析
  • OA系统中组织结构的处理
  • Junit单元测试框架
    • 底层设计:TestCase(叶子),TestUnit(容器),Test接口(抽象)
装饰模式 Decorator

动态地为一个对象增加新的功能。

用于代替继承,无需通过继承增加子类就能扩展对象的新功能。使用对象的关联关系代替继承关系,更加灵活,同时避免类型体系的快速膨胀。

实现细节
  • Component抽象构件角色 真实对象和装饰对象有相同的接口,客户端能以真实对象相同的方式同装饰对象交互。
  • ConcreteComponent具体构件角色 真实对象 io流中的FileInputStream FileOutputStream。
  • Decorator装饰角色 持有一个抽象构件的引用,装饰对象接收所有客户端的请求,并把这些请求转发给真实的对象,使得能在真实对象调用前后增加新的功能。
  • ConcreteDecorator具体装饰角色 负责给构件对象增加新的责任。
开发中使用的场景
  • IO中输入和输出流的设计。
  • Swing包中图形界面构件功能。
  • Servlet API中提供了一个request对象的Decorator设计模式的默认实现类HttpServletRequestWrapper类,增强了request对象的功能。
  • Strut2中,request、response、session对象的处理。
总结

别名:包装器模式Wrapper

降低系统的耦合度,可以动态的增加或删除对象的职责,并使得需要装饰的具体构建类和具体装饰类可以独立变化,以便增加新的具体构建类和具体修饰类。

优点

  • 扩展对象功能,比继承灵活,不会导致类个数急剧增加。
  • 可以对一个对象进行多次装饰,创造出不同行为的组合,得到功能更加强大的对象。
  • 具体构建类和具体装饰类可以独立变化,用户可以根据需要自己增加新的具体构件子类和具体装饰子类。

缺点

  • 产生很多小对象,大量小对象占据内存,一定程度上影响性能。
  • 装饰模式易于出错,调试排查比较麻烦。

装饰模式与桥接模式区别

两个模式对是为了解决多个子类对象问题,但诱因不同。桥接模式时对象自身现有机制沿着多个维度变化,是既有部分不稳定,装饰模式是为了增加新的功能。

外观模式 Facade

核心:为子系统提供统一的入口,封装子系统的复杂性,便于客户端调用。

应用场景

JDBC封装后,commons提供的DBUtils类,Hibernate提供的工具类、Spring JDBC工具类等。

享元模式 FlyWeight

场景:存在很多个相同或相似的对象,通过享元模式节省内存。

核心
  • 以共享的方式高效地支持大量细粒度对象的重用。
  • 享元对象能做到共享的关键是区分了内部状态和外部状态。
    • 内部状态:可以共享,不随环境变化而改变。
    • 外部状态:不可以共享,会随环境变化而改变。
实现
  • FlyWeightFactory 享元工厂类:创建并管理享元对象,享元池一半设计成键值对。
  • FlyWeight 抽象享元类:通常是一个接口或抽象类,声明公共方法,这些方法可以向外界提供对象的内部状态,设置外部状态。
  • ConcreteFlyWeight 具体享元类 为内部状态提供成员变量进行存储。
  • UnsharedConcreteFlyWeight 非共享享元类:不能被共享的子类可以设计为非共享享元类。
常用场景
  • 共享“池”:线程池,数据库连接池……
  • String类的设计也是享元模式
优点
  • 极大减少内存中对象的数量。
  • 相同或相似对象内存中只存一份,极大地节约资源,提高系统性能。
  • 外部状态相对独立,不影响内部状态。
缺点
  • 模式较复杂,使程序逻辑复杂化。
  • 为了节省内存,共享了内部状态,分离出外部状态,而读取外部状态使运行时间变长,用时间换取了空间。

行为型模式

  • 模板方法模式
  • 命令模式
  • 迭代器模式
  • 观察者模式
  • 中介者模式
  • 备忘录模式
  • 解释器模式
  • 状态模式
  • 策略模式
  • 责任链模式
  • 访问者模式
责任链模式 Chain of Responsibility

定义:将能够处理同一类请求的对象连成一条链,所提交的请求沿着链传递,链上的对象逐个判断是否有能力处理该请求,如果能则处理,如果不能则传递给链上的下一个对象

添加新的处理对象:由于责任链的创建完全在客户端,因此新增新的具体处理者对原有类库没有任何影响,只需添加新的类,然后再客户端调用时添加即可,符可开闭原则。

实现方式
  • 链表方式定义职责链
  • 非链表方式实现责任链:通过集合、数组生成职责链
常用场景
  • Java中异常机制,一个try可对应多个catch,当第一个catch不匹配类型,则自动跳到第二个catch
  • JavaScript语言中,事件的冒泡和捕获机制;Java中事件的处理采用观察者模式
  • Servlet开发中,过滤器的链式处理
  • Strut2中拦截器的调用
迭代器模式 Iterator

提供一种可以遍历聚合对象的方式,又称为游标模式 Cursor

  • 聚合对象:存储数据
  • 迭代器:遍历数据

正向遍历/逆向遍历

常用场景

JDK内置迭代器List/Set

中介者模式 Mediator
核心 [解耦]
  1. 如果一个系统中对象之间的联系呈现为网状结构,对象之间存在大量多对关系,将导致关系极其复杂。这些对象称之为同事对象。
  2. 引入中介者对象,使各个同时对象只跟中介者对象打交道,将复杂的网络结构化解为如下的星形结构。
本质

解耦多个同事对象之间的交互关系,每个对象都持有中介者对象的引用,只跟中介者对象打交道。

常见场景
  • MVC模式中,Control控制器就是一个中介者对象,Model和View都和它打交道。

  • 窗口游戏程序,窗口软件开发中窗口对象也是一个中介者对象

  • 图形界面开发GUI,多个组件之间的交互,可以通过引入一个中介者对象来解决,可以是整体的窗口对象或者DOM对象。

  • Java.lang.reflect.Method#invoke()

命令模式 Command

将一个请求封装为一个对象,从而使我们可用不同的请求对客户进行参数化;对请求排队或者记录请求日志,以及支持可撤销的操作。也称之为Action模式或Transaction模式。

结构
  • Command抽象命令类
  • ConcreteCommand具体命令类
  • Invoker调用者/请求者:请求的发送者,它通过命令对象来执行请求。一个调用者并不需要在设计时确定其接收者,因此它只与抽象命令类之间存在关联。在程序运行时,将调用命令对象的execute(),间接调用接收者的相关操作。
  • Receiver接收者 接收者执行与请求相关的操作,具体实现对请求的业务处理。 未抽象前,实际执行操作内容的对象。
  • Client客户类 在客户类中需要创建调用者对象、具体命令类对象,在创建具体命令对象时指定对应的接收者。发送者和接收者之间没有直接关系,都通过命令间接调用。
常见场景
  • Strut2中,action的整个调用过程中
  • 数据库事务机制的底层实现
  • 命令的撤销和恢复
解释器模式 Interpreter

用于描述如何构成一个简单的语言解释器,主要用于使用面向对象语言开发的编译器和解释器设计。

尽量不要使用解释器模式,后期维护麻烦很大。项目中,可以使用Jruby,Groovy,Java的js引擎来替代解释器的作用,弥补Java语言的不足。

常用场景
  • EL表达式的处理
  • 正则表达式解释器
  • SQL语法的解释器
  • 数学表达式解析器 工具包:Math Expression String Parser、Expression4J
访问者模式 Visitor

对于存储在一个集合中的对象,它们可能具有不同的类型(即使有一个公共的接口),对于该集合中的对象,可以接受一类称之为访问者的对象来访问,不同的访问者其访问方式也有所不同。

定义:表示一个作用于某对象结构中的各元素的操作,它使我们可以在不改变这个元素的类的前提下定义作用于这些元素的操作。

常用场景
  • XML文档解析器设计
  • 编译器的设计
  • 复杂集合对象的处理
策略模式 Strategy

条件语句,实现容易,维护难,不符合开闭原则。

策略模式对应于解决摸一个问题的算法族,允许用户从该算法族中任选一个算法解决莫伊问题,同时可以方便地更换算法或者增加算法。并由客户端决定调用哪个算法。

本质:分离算法,选择实现。

常见场景
  • JavaSE中GUI编程中布局管理
  • Spring框架中,Resource接口,资源访问策略
  • javax.servlet.http.HttpServlet#service()
模板方法模式 Template Method

定义了一个操作中的算法骨架。将某些步骤延迟到子类中实现。新的子类可以在不改变一个算法结构的前提下重新定义该算法的某些特定步骤。

核心

处理某个流程的代码已经都具备,但是其中某个节点的代码暂时不能确定。将结点的代码实现转移给子类完成。即处理步骤父类中定义好,具体实现延迟到子类中定义。

方法回调(钩子方法)

  • 好莱坞原则 "Don't call me,we'll call you back."
  • 子类不能调用父类,而通过父类调用子类。
常见场景
  • 数据库访问的封装
  • Junit单元测试
  • Servlet中doGet()和doPost()方法调用
  • Hibernate中模板程序
  • Spring中JDBCTemplate、HibernateTemplate等
状态模式 State

不同状态对应不同行为。

核心:用于解决系统中复杂对象的状态转换以及不同状态下行为的封装问题

结构
  • Context环境类:维护一个State对象,定义当前的状态
  • State抽象环境类
  • ConcreteState具体环境类:每一个类封装一个状态对应的行为
常见场景
  • 银行系统中帐号状态的管理
  • OA系统中公文状态的管理
  • 酒店系统中房间状态的管理
  • 线程对象各状态的切换
观察者模式 Observer

多个订阅者、客户称之为观察者;需要同给多个订阅者的数据封装到对象中,称之为目标。

核心

主要用于1:N的通知,当一个对象(Subject/Observable)的状态变化时,需要及时告知一系列对象(Observer),令它们做出响应。

通知观察者的方式

  • 推:每次都会把通知以广播方式发送给所有观察者,所有观察者只能被动接收。
  • 拉:观察者只要知道有情况即可,至于什么时候获取内容,获取什么内容都可以自主决定。

JavaSE提供了java.util.Observable和java.util.Observer来实现观察者模式。

常见场景
  • 聊天室程序,服务器转发给所有客户端
  • 网络游戏多人联机对战场景中,服务器将客户端的状态进行分发
  • 邮件订阅
  • Servlet中监听器的实现
  • Android中广播机制
  • JDK的AWT中事件处理模型基于观察者模式的委派事件模型(Delegation Event Model)
    • 事件源---目标对象
    • 事件监听器---观察者
  • 京东商城,群发某商品打折信息
备忘录模式 Memento

核心:保存某个对象内部状态的拷贝,恢复原先状态

结构

  • 源发器类 Originator
  • 备忘录类 Memento
  • 负责人类 CareTaker

分类

  1. 可通过列表保存多个备忘录类。
  2. 通过栈保存多个备忘点,或将多个备忘录对象序列化和持久化。
常见场景
  • 棋类游戏中的悔棋
  • 普通软件中的撤销操作
  • 数据库软件中,事务管理中的回滚操作
  • PhotoShop软件中的历史记录

思维导图

<iframe id="embed_dom" name="embed_dom" frameborder="0" style="display:block;width:100%; height:600px;" src="https://www.processon.com/embed/5f57a53163768959e2dcb3bb"></iframe>

About

GOF23种设计模式思维导图与代码示例笔记。

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages