从 JDK 中记设计模式
按字母排序,尽量找哪些简单的J2SE的实现。
-
Abstract Factory:提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。
例子:java.util.Calendar#getInstance(TimeZone)
提供一个时区参数即可从Calendar中创建需要的Calendar类的子类,而无需指定甚至知道它到底是哪个类调用的是哪个类的构造方法。 -
Adapter:将一个类的接口转换成客户希望的另外一个接口。Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。
例子:java.io.InputStreamReader(InputStream)
可以将任何InputStream类(如文本流,网络流,内存流)的接口(InputStream)转换成用户希望的Reader抽象类(接口)(InputStreamReader)。可以使得那些原本不兼容Stream但是接受Reader而的组件可以正常工作。 -
Bridge:将抽象部分与它的实现部分分离,使它们都可以独立地变化。
例子:java.awt.Button
按钮显示成什么样子(抽象部分)根据运行的系统来决定,点击按钮的行为(实现部分)由程序员自己写代码决定。它们均可以独立变化。 -
Builder:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
例子:java.lang.StringBuilder#append()
可以使用几乎任何对象来构造一个字符串对象。用户只需使用同样的方法而不需要知道传入的对象是如何被表示的即可构造一个字符串。 -
Chain of Responsibility:为解除请求的发送者和接收者之间耦合,而使多个对象都有机会处理这个请求。将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它。
例子:异常捕获机制
当方法的运行出现异常时,首先将这个异常传入本方法的catch块,如果搞不了(throw了)就传入调用这个方法的方法的catch块,如果还搞不了就传入调用调用这个方法的方法的方法的catch块……以此类推,这个方法的调用链中的每一个方法都有机会处理这个异常,沿着这条调用链传递这个异常,直到有一个方法能够处理这个异常为止。 -
Command:将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可取消的操作。
例子:java.awt.event.ActionListener
从被监听的对象(如按钮)的角度讲,将一个点击按钮要执行的代码封装成一个对象,从而使得不同的按钮可以执行不同的代码。同时也可以给一个按钮添加多个*********,此时当按钮被点击时,监听的对象排队运行ActionProformer代码,同时也可以给按钮调用removeActionListener来取消一些操作。 -
Composite:将对象组合成树形结构以表示“部分-整体”的层次结构。它使得客户对单个对象和复合对象的使用具有一致性。
例子:java.awt.Container#add(Component)
因为在窗口中可以框架套框架,所以窗体内部的组件事实上变成了一棵“控件树”。在add方法中,既可以提供单个的控件(如按钮),也可以提供一个由多个控件组合好的Panel,它们在代码中都是以Component的形象表示的。 -
Decorator:动态地给一个对象添加一些额外的职责。就扩展功能而言, 它比生成子类方式更为灵活。
例子:java.io.BufferedInputStream(InputStream)
仅仅是为InputStream添加了缓冲区的功能,但是它本质上还是InputStream,可以和之前的InputStream同样地使用。 -
Facade:为子系统中的一组接口提供一个一致的界面, Facade模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。
例子:java.lang.Class
无论一个类被设计的如何复杂,如果只需要获取这个类的名称,那么只需要为这个对象生成一个Class对象即可。 -
Factory Method:定义一个用于创建对象的接口,让子类决定将哪一个类实例化。Factory Method使一个类的实例化延迟到其子类。
例子:java.lang.Object#toString()
Object仅仅定义了返回String,由Object的子类来决定如何生成这个字符串,甚至可以生成字符串的子类(如果字符串不是final类的话)。 -
Flyweight:运用共享技术有效地支持大量细粒度的对象。
例子:java.lang.Integer#valueOf(int)
当内存中已经有一个相同数字的包装器时,这个方法不会构造一个新的包装器,而是返回现成的包装器,使得两个变量共享同一个数字包装器对象。所以即使调用了上万次valueOf同一个数字并分别赋给了不同的变量,内存中也只有一个包装器对象存在。 -
Interpreter:给定一个语言, 定义它的文法的一种表示,并定义一个解释器, 该解释器使用该表示来解释语言中的句子。
例子:java.text.DecimalFormat
它定义了一种显示数字的语言(#是可省略整数,0是不可省略整数,E是指数等),并通过这个对象来解释这种语言,使其用于格式化数字。 -
Iterator:提供一种一致的方法来顺序遍历一个容器中的所有元素。
例子:java.util.Iterator
不解释,直接查文档。 -
Mediator:用一个中介对象来封装一系列的对象交互。中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。
例子:java.lang.Thread
Thread类可以不直接指定线程主方法,而是通过指定实现Runnable接口的对象来指定,所以我们可以为一个Runnable对象指定多个Thread来多次定时执行同一个方法。 -
Memento:在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可将该对象恢复到保存的状态。
例子:java.util.Date#getTime()
它获取了Date对象的一个内部标识,其事实是一个整数,标明的是从1970年1月1日凌晨0点到这个Date对象所指的时间点度过的毫秒数,所以我们只需存储这个整数而不必存储整个对象,将来还可以通过这个整数(使用setTime方法)来恢复这个Date对象的状态。 -
Observer:定义对象间的一种一对多的依赖关系,以便当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并自动刷新。
例子:java.awt.event.ActionListener
从监听的对象的角度讲,当被监听的对象(如按钮)的状态发生改变(如被点击了)时,所有监听这个按钮的对象都得到了通知(调用了各自的ActionProform方法)并可以更新自己的状态。 -
Prototype:用原型实例指定创建对象的种类,并且通过拷贝这个原型来创建新的对象。
例子:java.lang.Object#clone()
当clone方法被正确实现后,创建一个对象的方式可以是clone现有的对象,并修改clone后的对象来进行创建的操作。 -
Proxy:为其他对象提供一个代理以控制对这个对象的访问。
例子:java.lang.reflect.Proxy
-
Singleton:保证一个类仅有一个实例,并提供一个访问它的全局访问点。
例子:java.awt.Desktop
当要对桌面进行操作的时候,不能自行创建新的桌面,只能操作现有的唯一桌面,并有一个全局的访问此桌面的方法(java.awt.Desktop.getDesktop())。 -
State:允许一个对象在其内部状态改变时改变它的行为。对象看起来似乎修改了它所属的类。
例子:java.util.Scanner
当一个Scanner对象的hasNextByte为真时,它(看起来)就变成了一个ByteScanner(调用next时返回一个byte包装器),当hasNextBoolean为真时,它(看起来)就不是ByteScanner了,而变成了一个BooleanScanner(调用next时返回一个boolean包装器),以此类推。 -
Strategy:定义一系列的算法,把它们一个个封装起来, 并且使它们可相互替换。本模式使得算法的变化可独立于使用它的客户。
例子:java.util.Comparator
不同的排序算法被封装成了不同的Comparator,当使用Collections.sort方法时,传入不同的Comparator即可实现按照不同的排序算法对集合进行排序。于是排序算法相对于被排序的对象被独立出来了。 -
Template Method:定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。Template Method使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
例子:java.io.InputStream#skip()
InputStream的Skip方法定义了一个骨架(如流指针的位置原来是0,skip了2个位置之后,只要没到流尾,无论是什么流都会将位置设为2),但是具体skip的操作细节(如何skip)由子类自己来决定。 -
Visitor:表示一个作用于某对象结构中的各元素的操作。它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作。
例子:javax.lang.model.element.Element 和javax.lang.model.element.ElementVisitor