参考LeetCode的文章:
设计模式基于六大原则:
- 开闭原则:一个软件实体如类、模块和函数应该对修改封闭,对扩展开放。
- 单一职责原则:一个类只做一件事,一个类应该只有一个引起它修改的原因。
- 里氏替换原则:子类应该可以完全替换父类。也就是说在使用继承时,只扩展新功能,而不要破坏父类原有的功能。
- 依赖倒置原则:细节应该依赖于抽象,抽象不应依赖于细节。把抽象层放在程序设计的高层,并保持稳定,程序的细节变化由低层的实现层来完成。
- 迪米特法则:又名「最少知道原则」,一个类不应知道自己操作的类的细节,换言之,只和朋友谈话,不和朋友的朋友谈话。
- 接口隔离原则:客户端不应依赖它不需要的接口。如果一个接口在实现时,部分方法由于冗余被客户端空实现,则应该将接口拆分,让实现类只需依赖自己需要的接口方法。
构建型模式
总共五种:
- 工厂方法模式
- 抽象工厂模式
- 单例模式
- 建造型模式
- 原型模式
单例模式
建造型模式
常见应用:StringBuffer、StringBuilder
原型模式
应用:Object 类 的clone()
结构型模式
- 适配器模式
- 桥接模式
- 组合模式
- 装饰模式
- 外观模式
- 享元模式
- 代理模式
适配器模式
应用:java.util.Arrays#asList()、java.util.Collections#list()、java.util.Collections#enumeration()
桥接模式
应用:JDBC
组合模式
应用:java.util.Map#putAll(Map)、java.util.List#addAll(Collection)、java.util.Set#addAll(Collection)
外观模式
应用
java.io.BufferedInputStream(InputStream)
java.io.DataInputStream(InputStream)
java.io.BufferedOutputStream(OutputStream)
参考:设计模式 - 装饰.md
享元模式
应用:java.lang.Integer#valueOf(int)
参考:设计模式 - 享元.md
代理模式
参考:java.lang.reflect.Proxy
设计模式 - 代理.md
行为型模式
- 责任链模式
- 命令模式
- 解释器模式
- 迭代器模式
- 中介者模式
- 备忘录模式
- 观察者模式
- 状态模式
- 策略模式
- 模板方法模式
- 访问者模式
责任链模式
责任链主要用于处理职责相同,程度不同的类。
优点:
- 降低了对象之间的耦合度。在责任链模式中,客户只需要将请求发送到责任链上即可,无须关心请求的处理细节和请求的传递过程,所以责任链将请求的发送者和请求的处理者解耦了。
- 扩展性强,满足开闭原则。可以根据需要增加新的请求处理类。
- 灵活性强。可以动态地改变链内的成员或者改变链的次序来适应流程的变化。
- 简化了对象之间的连接。每个对象只需保持一个指向其后继者的引用,不需保持其他所有处理者的引用,这避免了使用众多的条件判断语句。
- 责任分担。每个类只需要处理自己该处理的工作,不该处理的传递给下一个对象完成,明确各类的责任范围,符合类的单一职责原则。不再需要 “项目经理” 来处理所有的责任分配任务。
缺点:
- 不能保证每个请求一定被处理,该请求可能一直传到链的末端都得不到处理。
- 如果责任链过长,请求的处理可能涉及多个处理对象,系统性能将受到一定影响。
- 责任链建立的合理性要靠客户端来保证,增加了客户端的复杂性,可能会由于责任链拼接次序错误而导致系统出错,比如可能出现循环调用。
常见应用:Filter
命令模式
命令模式可以说将封装发挥得淋漓尽致。在我们平时的程序设计中,最常用的封装是将拥有一类职责的对象封装成类,而命令对象的唯一职责就是通过 execute 去调用一个方法,也就是说它将 “方法调用” 这个步骤封装起来了,使得我们可以对 “方法调用” 进行排队、撤销等处理。
优点:
- 降低系统的耦合度。将 “行为请求者” 和 ”行为实现者“ 解耦。
- 扩展性强。增加或删除命令非常方便,并且不会影响其他类。
- 封装 “方法调用”,方便实现 Undo 和 Redo 操作。
- 灵活性强,可以实现宏命令。
缺点:
- 会产生大量命令类。增加了系统的复杂性。
应用:java.lang.Runnable
参考:设计模式 - 命令.md
解释器模式
应用:java.util.Pattern
迭代器模式
定义:
迭代器模式(Iterator Pattern):提供一种方法访问一个容器对象中各个元素,而又不需暴露该对象的内部细节。
应用:java.util.Iterator、java.util.Enumeration
中介者模式
中介者模式就是用于将类与类之间的 多对多关系 简化成 多对一、一对多关系 的设计模式,定义如下:
中介者模式(Mediator Pattern):定义一个中介对象来封装一系列对象之间的交互,使原有对象之间的耦合松散,且可以独立地改变它们之间的交互。
应用:
java.util.concurrent.Executor#execute()
submit() and invokeXXX() methods of java.util.concurrent.ExecutorService
java.lang.reflect.Method#invoke()
备忘录模式
定义:
备忘录模式:在不破坏封装的条件下,通过备忘录对象存储另外一个对象内部状态的快照,在将来合适的时候把这个对象还原到存储起来的状态。
优点:
- 给用户提供了一种可以恢复状态的机制,可以使用户能够比较方便的回到某个历史的状态
- 实现了信息的封装,使得用户不需要关心状态的保存细节
缺点:
- 消耗资源,如果类的成员变量过多,势必会占用比较大的资源,而且每一次保存都会消耗一定的内存。
应用:java.io.Serializable
参考:设计模式 - 备忘录.md
观察者模式
观察者模式(Observer Pattern):定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。
观察者模式非常常见,近年来逐渐流行的响应式编程就是观察者模式的应用之一。
当公众号发表一篇文章,所有关注了公众号的读者立即收到了文章,这个过程中所有关注了公众号的微信客户端就对公众号发表文章的事件做出了响应,这就是一个典型的一对多观察者模式。
应用:java.util.Observer、java.util.EventListener
状态模式
状态模式(State Pattern):当一个对象的内在状态改变时允许改变其行为,这个对象看起来像是改变了其类。
用户状态改变后,行为也随着改变了,这就是状态模式定义的由来,它的优点是:将与特定状态相关的行为封装到一个状态对象中,使用多态代替 if-else 或者 switch-case 状态判断。缺点是必然导致类增加,这也是使用多态不可避免的缺点。
参考:设计模式 - 状态.md
策略模式
策略模式(Strategy Pattern):定义了一系列算法,并将每一个算法封装起来,而且使它们还可以相互替换。策略模式让算法独立于使用它的客户而独立变化。
策略模式用一个成语就可以概括 —— 殊途同归。当我们做同一件事有多种方法时,就可以将每种方法封装起来,在不同的场景选择不同的策略,调用不同的方法。
以排序算法为例。排序算法有许多种,如冒泡排序、选择排序、插入排序,算法不同但目的相同,我们可以将其定义为不同的策略,让用户自由选择采用哪种策略完成排序。
策略模式与状态模式非常类似,甚至他们的 UML 类图都是一模一样的。两者都是采用一个变量来控制程序的行为。策略模式通过不同的策略执行不同的行为,状态模式通过不同的状态值执行不同的行为。两者的代码很类似,他们的区别主要在于程序的目的不同。
- 使用策略模式时,程序只需选择一种策略就可以完成某件事。也就是说每个策略类都是完整的,都能独立完成这件事情,如上文所言,强调的是殊途同归。
- 使用状态模式时,程序需要在不同的状态下不断切换才能完成某件事,每个状态类只能完成这件事的一部分,需要所有的状态类组合起来才能完整的完成这件事,强调的是随势而动。
应用:
java.util.Comparator#compare()
javax.servlet.http.HttpServlet
javax.servlet.Filter#doFilter()
参考:设计模式 - 策略.md
模板方法模式
模板方法模式(Template Method Pattern):定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
通俗地说,模板方法模式就是一个关于继承的设计模式。
在使用模板方法模式时,我们可以为不同的模板方法设置不同的控制权限:
- 如果不希望子类覆写模板中的某个方法,使用 final 修饰此方法;
- 如果要求子类必须覆写模板中的某个方法,使用 abstract 修饰此方法;
- 如果没有特殊要求,可使用 protected 或 public 修饰此方法,子类可根据实际情况考虑是否覆写。