设计模式在java中的运用


读《java与模式》之一




设计模式在java中的运用---part 1_java


第 I 条      理解三种工厂模式,关键是工厂方法。(区分factory method和factory method pattern)


          简单工厂模式:


又称静态工厂方法模式,其工厂方法是静态方法。


   

设计模式在java中的运用---part 1_iterator_02


creator必须包含逻辑判断,以决定在何时,创建何种类的实例。Client只有消费的责任。因此,简单工厂分解了责任。


何时创建与创建何种对象,这两种逻辑混合在一起了,工厂方法模式解决该问题;再,简单工厂中以静态方法为工厂方法,其问题在于不能被 override (参考 ),工厂方法模式解决该问题。


java.text.DateFormat java.text.SimpleDataFormat


    DateFormat.getTimeInstance();


    DateFormat.getTimeInstance(int style);


    DateFormat.getTimeInstance(int style, Locale locale);



设计模式在java中的运用---part 1_byte_03


    DateFormat是抽象类,上述三个方法都是静态方法,查源码知:三个方法最后创建的是SimpleDateFormat对象,即DateFormat的子类对象。因此,DateFormat即是creator,又是AbstractProduct,SimpleDateFormat是ConcreteProduct。getTimeInstance是工厂方法。


 


          工厂方法模式:


    引入抽象工厂类,由其子类完成产品对象的创建,而不是像在简单工厂中由一个工厂类完成所有产品对象的创建。因此,工厂类可以和产品类形成平行的等级结构。



设计模式在java中的运用---part 1_产品_04


java.util.Collection接口中定义了创建产品Iterator的工厂方法iterator()。每个继承Collection的子类,有其各自的iterator(),并创建不同类型的具体Iterator。



设计模式在java中的运用---part 1_java_05


          抽象工厂模式


对比抽象工厂模式和工厂模式,可以发现在前者,一个具体工厂有多个创建方法(这里是工厂方法,注意工厂方法和工厂方法模式),而后者只有一个创建方法。更进一步,在前者,一个具体工厂对应多个具体产品,而在后者,一个具体工厂对应一个具体产品。这是两者在形式上的区别。



设计模式在java中的运用---part 1_产品_06


简单工厂模式中,一个工厂类创建所有的产品类;工厂方法模式中,这个工厂类被抽象,由其子类,即具体工厂创建具体产品,且一一对应;抽象工厂模式将创建方法再抽象,一个具体工厂创建多个具体产品,这些具体产品通常被认为是有关联的,即是一个产品族。


 


第 II 条命令模式


请求方发请求要求执行某操作;接收方收到请求执行该操作。


命令的请求与执行被分开,请求方和接收方可以独立演化,因此:


新命令容易添加;


接收方可以否决请求;


容易设计命令队列;


容易实现命令的undo和redo;


容易将命令记录到日志。



设计模式在java中的运用---part 1_java_07


关于命令模式的使用:


命令对象的生命期,不受发出命令的请求对象的生命期限制。


命令模式可以支持使用undo和redo,以及veto。前两者要结合Memento模式。


有了命令模式可以支持将操作写入日志,这样便于系统崩溃时重做。


在transaction中使用命令模式,可以增加新的transaction。


将多个命令聚会在一起,即结合Composite模式,可以组成宏命令。


为了复制命令,结合Prototype模式。


一个问题:


可能导致过多的具体命令类的存在。


 


AWT 的事件处理机制


在该机制中,命令对象(具体命令)实现AWT的listener接口(命令接口)。事件是请求者,组件是接收者。AWT中未将接收者组件的引用放在命令中,而是使用事件委派机制另外管理。先以事件注册的方式指明对某请求,命令和接收者是如何的对应关系。当某事件发生,发出某具体命令,从事先的注册中查找该命令对应的接收者组件,然后调用之。


    AWT使用事件注册/分派机制是必要的,因为不能事先确定事件发出的命令将有多少接收者。在程序中就是使用addListener()动态添加接收者。


 


Swing 的命令 undo redo    


第 III 条          Singleton模式与multiton模式


对singleton几点注意:


          三种singleton:饿汉式,懒汉式,登记式。


登记式:

设计模式在java中的运用---part 1_byte_08


          Singleton类可以分为:有状态、无状态。有状态的singleton可用作状态库repository。属性管理器常使用singleton模式。


          不完全的singleton类:如果singleton类存在public的构造器,那么除了使用工厂方法外(singleton所希望),还是从client调用构造器创建singleton对象。这个singleton就是不完全singleton。


Java.lang.Runtime对象也是singleton模式,每个java应用程序有唯一的Runtime对象( Runtime.getRuntime() )。


java.awt.Toolkit也是singleton模式。


 


Multiton模式是singleton模式的推广:


多例类可以有多个实例;


多例类自己创建、管理自己的实例、向外界提供自己的实例。


Multiton又分:有上限多例类、无上限多例类;有状态、无状态。


为了保存多个实例,multiton类可使用一个聚集对象记录创建的各实例。这与singleton的登记式类似。但是,singleton的登记式的用意是改善singleton不能继承的问题,不论是其中的父类还是子类还是只能有一个实例的。


 


应用案例:见《java与模式》


多例模式与多语言支持、序列键生成器与单例多例模式


第 IV 条          Proxy模式



设计模式在java中的运用---part 1_iterator_09


java.lang.reflect.Proxy和java.lang.reflect.InvocationHandlers现实java的动态代理机制。


java的动态代理机制需要进一步研究:


http://www.javaworld.com/javaworld/jw-11-2000/jw-1110-proxy.html?page=1


第 V 条Builder模式


Builder模式可以将一个产品的internal representation与产品的创建过程分开,从而一个创建过程生成具有不同internal representation的产品对象。


设计模式在java中的运用---part 1_设计模式_10


builder模式有很多种情况的退化和变化,如builder与director的合并。


 


javamail利用builder模式创建一个待发送的消息,如下片段对message的创建:



设计模式在java中的运用---part 1_java_11


第 VI 条          Observer模式


又叫发布/订阅模式(publish/subscribe),多个观察者对象同时监听一个主题对象。当主题对象状态改变时,所有观察者对象收到来自主题对象的通知。


设计模式在java中的运用---part 1_iterator_12


 


java.util.Observer接口和java.util.Observable类是observer模式的完美再现。


AWT,servlet的DEM机制(Delegation Event Mode)。


javax.swing.Timer类


第 VII 条     Composite模式


Composite使client将叶子节点与树枝节点同等对待。


有两种Composite:安全模式和透明模式。前者在Componet中声明管理子类对象的方法;后者在Composite中声明管理子类对象的方法。



设计模式在java中的运用---part 1_iterator_13


AWT和Swing的图形界面的结构是基于Composite的:



设计模式在java中的运用---part 1_设计模式_14


第 VIII 条              Bridge模式



设计模式在java中的运用---part 1_产品_15


将抽象化(Abstraction)与实现化()implementation分脱耦,使得二者可以独立地变化。


     抽象与实现在面向对象系统设计中的重要方式,是实现“开闭原则OCP”的重要武器,通常可以用继承来完成。但继承的缺点是父类与子类的耦合度高,因此经常把继承用组合/聚集来替代。Bridge模式就是如此。


    值得注意的是:Implementor中的OperationImlp()不一定要一一对应于Abstraction的Operation(),实际上两个接口Implementor和Abstraction可以很不一样。要点是:Implementor给出底层操作,而Abstraction给出基于底层的高层操作。


 


    再论OCP:OCP要求对扩展开,对修改闭。抽象化与实现化的最简单方式就是继承。继承可以在子类的层次对扩展开放,而父类层次上也扩展的情况就不能处理了。



设计模式在java中的运用---part 1_产品_16


因此,为了让父类与子类两个层次上的变化独立,


          各自继承:



设计模式在java中的运用---part 1_设计模式_17


          用聚集关联:



设计模式在java中的运用---part 1_java_18


这样:两个层次上的变化被隔离了。


设计提示:


          好的设计通常没有多于两层的继承;


          当变化有两个以上的时候,注意区分使用继承还是聚合。


 


Bridge 模式是“对变化封装”原则和组合 / 聚集复用原则的极好范例。


 


AWT的peer架构:



设计模式在java中的运用---part 1_java_19



设计模式在java中的运用---part 1_iterator_20


 


JDBC驱动器:



设计模式在java中的运用---part 1_byte_21


其他驱动器:


很多驱动器软件都是bridge模式,如打印机驱动器。


第 IX 条          Adapter模式


将一个类的接口变成client期望的另一种接口,从而让因接口不同而不能合作的类能够一起工作。


对象的adapter:



设计模式在java中的运用---part 1_设计模式_22


类的adapter:



设计模式在java中的运用---part 1_产品_23


 


java在没有collection框架前(before 1.2)使用Enumeration接口,而之后使用Iterator接口。两个接口具有不同的方法,用adapter匹配:


从Enumeration到Iterator:



设计模式在java中的运用---part 1_设计模式_24


从Iterator到Enumeration:



设计模式在java中的运用---part 1_产品_25


另外的例子:


WINE:在linux上运行windows程序的平台。


MKS Toolkit:在windows上运行unix shell的平台。


 


J2se中存在很多的默认适配器类。


第 X 条   Decorator模式


又名wrapper,对client透明地扩展对象的功能,是继承的一个替代方案。可以最对对象添加功能,而不是对整个类。



设计模式在java中的运用---part 1_设计模式_26


 


java的io库详解:


流就是数据的有序排列。


如下是io流的示意。



设计模式在java中的运用---part 1_java_27

设计模式在java中的运用---part 1_设计模式_28


IO 库的设计原则:


两个对称性:


         输入、输出对称:InputStream和OutputStream是Byte流输入、输出的两个平行等级结构的根部;Reader和Writer是char流输入、输出的根部。


         Byte、char对称:InputStream,Reader负责byte、char的输入;OutputStream、Writer负责byte、char的输出。


两个设计模式:


l         装饰模式:因为IO库需要很多的功能组合,继承会造成类爆炸。 IO 库就是由基本的原始流处理器和围绕它们的装饰流处理器(也称链接流处理器)组成的。


         适配器模式:


 


装饰模式的使用:


输入流:



设计模式在java中的运用---part 1_java_29


如上所示的输入流类图中,有四个原始流处理器,其他是装饰流处理器(能接受其他流,并用装饰模式增加了功能)


原始流处理器:


ByteArrayInputStream,FileInputStream,PipedInputStream,StringBufferInputStream


装饰流处理器:


FilterInputStream(BufferedInputStream,DataInputStream,LineNumberInputStream,PushbackInputStream),ObjectInputStream,SequenceStream


上图的装饰器模式结构如下:



设计模式在java中的运用---part 1_iterator_30


又可以抽象为:



设计模式在java中的运用---part 1_java_31


对应于输出流:



设计模式在java中的运用---part 1_byte_32


原始输出流:


ByteArrayOutputStream,FileOutputStream,PipedOutputStream


装饰器输出流:


FilterOutputStream(BufferedOutputStream,DataOutputStream,PrintStream),ObjectOutputStream。


上图的装饰器模式结构;



设计模式在java中的运用---part 1_产品_33


Reader的装饰模式


设计模式在java中的运用---part 1_设计模式_34


原始流处理器:


CharArrayReader,InputStreamReader(FileReader),PipedReader,StringReader


链接流处理器


BufferedReader(LineNumberReader),FilterReader(PushbackReader)


Writer的装饰器模式:



设计模式在java中的运用---part 1_iterator_35


原始流处理器:


CharArrayWriter,OutputStreamWriter(FileWriter),PipedWriter,StringWriter


链接流处理器:


BufferedWriter,FilterWriter,PrintWriter


 


半透明的装饰模式介于装饰模式和适配器模式之间:



设计模式在java中的运用---part 1_byte_36


理想的装饰器模式增强功能,而不改变接口。但这很难做到,因此常常增加某些方法,这就称为半透明的装饰模式。


适配器模式就是为了将接口不同而功能类似的类统一起来,所以适配器模式改变接口而不改变功能。


半透明的装饰器模式增加了功能,又适当改变了接口(增加),因此是介于上两者间的模式。


 


IO库中的适配器模式:


InputStream 体系为例:


         ByteArrayInputStream使用了适配器模式。



设计模式在java中的运用---part 1_产品_37


ByteArrayInputStream(上图应为ByteArrayInputStream)将byte数组的接口适配为了InputStream流处理器的接口。


         FileInputStream将FileDescriptor适配为了InputStream。



设计模式在java中的运用---part 1_iterator_38


         StringBufferInputStream将String适配为InputStream



设计模式在java中的运用---part 1_产品_39


OutputStream 体系为例:


         ByteArrayOutputStream将Array数组适配为OutputStream


         FileOutputStream将FileDescriptor适配为OutputStream


         StringBufferOutputStream将String适配为OutputStream


PipedOutputStream PipedInputStream 适配为 OutputStream


         PipedOutputStream将PipedInputStream总是一起使用的。



设计模式在java中的运用---part 1_byte_40


Reader 原始流处理器都是适配器模式


         CharArrayReader ß àChar array


         StringReader ß àString


         InputStreamReader从byte流到char流的适配器


Writer 原始流处理器都是适配器模式


         CharArrayWriter ß àChar array


         StringWrite ß àStringBuffer


         InputStreamWriter从byte流到char流的适配器


PipedWriter PipedReader 适配为 Writer


         两者总是一起使用的。



设计模式在java中的运用---part 1_iterator_41


 


byte 流到 char 流的适配


InputStreamReader是InputStream到Reader的适配





设计模式在java中的运用---part 1_iterator_42

设计模式在java中的运用---part 1_产品_43




OutputStream是OutputStream到Writer的适配





设计模式在java中的运用---part 1_byte_44

 

设计模式在java中的运用---part 1_java_45




一个例子:


String line;


InputStreamReader reader = new InputStreamReader(System.in);


System.out.println(“some strings”);


BufferedReader bfreader = new BufferReader(reader);


line = bfreader.readLine();


System.out.println(line);



设计模式在java中的运用---part 1_iterator_46

 


InputStreamReader 是适配器, BufferedReader 是装饰器。