前言
最近有很多同学想让我整理下关于设计模式的内容,今天就将设计模式的核心要掌握的设计模式它们的基本原理和使用场景 简要述说一下。可能基础不是足够扎实的知道的没有这么全,不过也不影响各位编码。
个人认为设计模式只是一个方法论、理论 供参考,让你能更好的、更方便的支撑业务,没必要花过多的功夫去写各种资料的demo
不然的话,相信我,用到项目中你还是不会信不信。
设计模式 7 大原则
单一职责 : 每个类只负责一个职责(或每个方法)
接口隔离 : 一个类对另一个类的依赖应建立在最小的接口上 依赖倒转: 高层模块不应依赖低层模块, 二者都应该依赖接口而非细节. 细节依赖抽象, 面向接口编程
里式替换 : 子类应该做到可以替换父类, 及子类应尽量不重写父类方法. 开闭原则: 对提供者而已可以修改, 对使用者而言不需要修改(即代码兼容性), 尽量使用扩展增加功能, 而非修改原有类
迪米特法则 : 一个对象应该对其他对象保持最小了解(最少知道原则)
合成复用原则 : 一个类使用另一个类的代码(方法), 尽量使用合成, 而不是继承
一、创建型
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
, 避免空对象判断, 避免空指针异常。
作者:橘松Java