一、创建型
1.1 单例模式
原理 : 确保一个类只有一个实例,并提供该实例的全局访问点。
饿汉式 : 静态常量 静态代码块
懒汉式
直接判断(线程不安全) 方法加 synchronized(线程安全, 效率低)
判断后再同步(错误写法)
双重判断(if-同步-if) (推荐写法)
匿名静态内部类 (简单, 推荐)
枚举(简单, 但对象方法写在枚举中, 略有不适)
示例:
java.lang.Runtime#getRuntime()
、java.awt.Desktop#getDesktop()
1.2原型模式
原理 : 使用原型实例指定要创建对象的类型,通过复制这个原型来创建新对象. 示例: Java
的 Object
对象的 clone
方法, java.util.Arrays.ArrayList#toArray()
浅拷贝 : 仅对基础类型及字符串类型的字段拷贝值
深拷贝 : 同时对引用类型(如数组,对象) 也进行拷贝
深拷贝实现: 1.重写 clone
, 一一处理每个引用对象(调用对象的 clone
), 麻烦, 且若对象之间关系复杂, 其中一个未实现深拷贝则导致 bug 2.利用序列化和反序列化, 如 Json
, 或 Java
自带的序列化方式(二进制)
1.3 创建者模式(生成器模式)
原理 :
封装一个对象的构造过程,并允许按步骤构造.
若对象的生成过于复杂(字段极多且赋值还有依赖关系, 需要顺序调用), 则可将赋值过程封装成一个build()
, 并放到一个 Builder
类中. 此类对外提供各个字段的赋值方法并先保存起来, 直到调用 build()
, 此方法返回对象实例。
使用此模式, 调用者无需关注构建过程, 只需设置自己想要的值, 然后调用 build()
即可得到对象实例. 且若增加或修改字段, 构造过程变化, 调用者无感知, 无需修改代码. 符合开闭原则。
示例:
StringBuilder
, 一些框架的ConfigurationBuilder
, 用于构建配置。
1.4 简单工厂模式
原理 : 在创建一个对象时不向客户暴露内部细节,并提供一个创建对象的通用接口。此模式可避免多个调用者创建对象时判断创建哪个子类的重复代码, 且若多一个子类, 调用者无需修改代码.
示例:
Spring ApplicationContext
的getBean
方法.
1.5 工厂方法模式
原理 : 定义了一个创建对象的接口,但由子类决定要实例化哪个类。工厂方法把实例化操作推迟到子类。 此模式解决了简单工厂每增加一个子类需要修改工厂类的问题。此模式存在问题, 若新增一个子类, 需同时新增一个子类工厂, 系统复杂性更高.
示例:
Calendar
,NumberFormat
1.6 抽象工厂模式
原理 : 提供一个接口,用于创建 相关的对象家族。同上, 由子类工厂决定创建哪些对象。此模式是工厂方法的升级版, 不同之处在于它同时创建多个种类的对象(工厂类具有多个方法)。
此模式将一个对象家族的新建集合到一个工厂类创建管理, 这些对象家族相互之间一般有关联, 在创建时就可以处理这些关联. 且对于 2 个子类工厂, 一般可以无缝切换, 使得修改代码极为方便(即换一个子类工厂)。
此模式在新增一个对象家族的成员时非常麻烦(即所有工厂类需要新增一个方法), 但再新增一类对象家族时比较简单(即新增一个子类工厂)。
二、结构型
2.1 适配器模式
原理 : 把一个接口转换成另一个用户需要的接口。定义一个类, 实现用户需要的接口, 并聚合一个需要转换的接口对象, 在重写的方法(用户需要的方法)中调用聚合的对象的方法, 若需要返回值, 且返回值类型不一致, 则还需要在方法中处理一番, 然后返回. 这个过程叫做适配.这个类叫做适配器类。使用此模式可对一些老旧接口适配兼容。
示例:
java.util.Arrays#asList()
将数组适配成List
,Spring MVC
的HandlerAdapter
2.2 装饰者模式
原理 :
将一个或多个功能(方法)动态的新增到一个类中。把需要新增功能类称为 A
,定义一个类B
,实现A
的上层接口, 并聚合一个A
的实例对象, B
类实现的接口中, 对其他不关心的方法直接调用聚合的对象的方法。对于关心的方法则可以在调用前后进行加料处理(如一个方法返回一个数, 可以在原来的返回值上乘以 2), 同时, B类也可以新增一些其他方法, 这些方法就是多出的功能。B
类就是装饰者类, A
就是被装饰类。
此模式的优点是, 装饰类也可以当做被装饰类, 然后再来一层装饰, 可以无限的装饰。
示例:
java IO
流
2.3 代理模式
原理 :
控制其他对象的访问(方法级), 将一些前置或后置的处理, 通过代理对象注入到目标对象的方法前后. 面向切面编程.
类型 :
静态代理 : 定义一个代理类实现目标对象的上层接口, 并聚合一个目标对象, 重写方法时将前置后置处理加上.
动态代理 :
JDK 动态代理
: 需要目标对象有上层接口(自然接口内的方法才可以代理) 使用java.lang.reflect.Proxy#getProxyClass
CGLIB动态代理
: 是个类就行。 实现原理是 ASM
框架动态生成目标对象类的子类字节码, 然后通过反射生成代理对象.
示例:
Spring AOP
2.4 桥接模式
原理 :
将抽象与实现分离开来,使它们可以独立变化。桥接的含义是, 一个桥, 放在哪里都有桥的 2 边, 桥的 2 边可以变化, 但桥始终不变. 此处, 桥代表一个操作(如手机上运行软件), 2 边代表 一个操作的 2 个维度(如手机和软件). 同时, 桥接后的操作也可以视为一个维度, 与另一个维度桥接(如手机上运行软件和人这 2 个维度, 可以进行桥接, 组成 3 维度嵌套桥接).
示例:
JDBC
获取连接, 获取连接是一个维度, 数据库是一个维度, 数据库有多个, 所以这是一个数据库维度变化, 另一维度不变的桥接模式。
2.5 享元模式
原理 :
利用共享的方式来支持大量细粒度的对象,这些对象一部分内部状态是相同的。如常见的 线程池, 常量池等, 使得对象的获取速度加快。
示例: java.lang.Integer#valueOf() java.lang.Boolean#valueOf()
2.6 组合模式
原理 :
将对象组合成树形结构来表示“整体/部分”层次关系,允许用户以相同的方式处理单独对象和组合对象。一般需要部分和整体具有一定的相似度, 才能对其进行抽象.对部分/整体进行抽象, 得出一个公共抽象类或接口, 再实现类中根据具体角色做不同处理。
示例:
java.util.Map#putAll(Map)
、java.util.List#addAll(Collection)
java.util.Set#addAll(Collection)
2.7 外观模式
原理 :
提供了一个统一的接口,用来访问子系统中的一群接口,从而让子系统更容易使用。
三、行为型
3.1 职责链(责任链)模式
原理 : 使多个对象都有机会处理请求,将这些对象连成一条链,并沿着这条链发送该请求,直到有一个对象处理它为止, 从而避免请求的发送者和接收者之间的耦合关系。
示例:
javax.servlet.Filter#doFilter()
、netty 的 Handler Chain
3.2 观察者模式
原理 : 定义对象之间的一对多依赖,当一个对象状态改变时,它的所有依赖都会收到通知并且自动更新状态。主题(Subject
)是被观察的对象,而其所有依赖者(Observer
)称为观察者。
示例:
swing
的事件监听(按钮事件, 鼠标事件)、JS
的 事件监听
3.3 状态模式
原理 : 允许对象在内部状态改变时改变它的行为,对象看起来好像修改了它所属的类。状态模式主要是用来解决状态转移的问题,当状态发生转移了,那么 Context
对象就会改变它的行为。
3.4 策略模式
原理 : 定义一系列算法,封装每个算法,并使它们可以互换。策略模式可以让算法独立于使用它的客户端。 策略模式主要是用来封装一组可以互相替代的算法族,并且可以根据需要动态地去替换 Context
使用的算法。
示例:
java.util.Comparator#compare()
、javax.servlet.http.HttpServlet
3.5 模板方法模式
原理: 定义算法框架,并将一些步骤的实现延迟到子类。通过模板方法,子类可以重新定义算法的某些步骤,而不用改变算法的结构。
示例:
java.util.Collections#sort()
3.6 命令模式
原理 : 将一个对象(命令接收者)的每个操作拆分到每一个命令类中, 再使用一个命令管理类来管理这些命令。 使得命令可以放入队列中有序执行, 且可以统一记录命令的操作日志, 还可以支持撤销操作(每个命令都实现对应的撤销即可)
此模式的好处是,若将命令抽象为几个标准的命令(如开,关), 然后管理多个命令接收者(如灯,电视机,空调)的操作, 可使新增命令接收者变得简单, 即扩展性好又称万能遥控器。
3.7 中介模式
原理 : 集中相关对象之间复杂的沟通和控制方式。降低子系统之间的耦合。类似一个消息收发中心, 负责字系统的消息中转, 使得子系统之间可以进行一定的交互。
示例: 线程池管理者线程和要执行的任务。
3.8 备忘录模式
原理 : 在不违反封装的情况下获得对象的内部状态,从而在需要时可以将对象恢复到最初状态。如对游戏的当前状态进行一个保存, 然后在后续游戏中死亡后可以读取这个状态重新开始。
3.9 访问者模式
原理 : 为一个对象结构(比如组合结构)增加新能力。使用访问者模式可实现重载的动态绑定(即伪双分派), 效果与重载方法内使用 instanceof
是一样的, 但使用访问者模式, 可扩展性更好。
3.10 迭代器模式
原理 : 提供一种顺序访问聚合对象元素的方法,并且不暴露聚合对象的内部表示。
示例:
java.util.Iterator
3.11 解释器模式
原理 : 为语言创建解释器,通常由语言的语法和语法分析来定义。
示例:
EL 表达式
,Freemaker模板
3.12 空对象模式
原理 : 使用什么都不做的空对象来代替 NULL
, 避免空对象判断, 避免空指针异常。