http://www.ibm.com/developerworks/cn/java/l-jdkdp/part2/
简介:
在
上一部分的内容中,我们讲到什么是模式,什么是设计模式,以及对一个设计模式Observer的详细阐叙。相信大家对于模式的概念应该是比较的理解了。这
部分及以后的内容,将会步入正题,从Java类库的分析入手,来阐叙设计模式是如何应用到一个完美的设计中的。实际上,Java类库非常的庞杂,这儿不可
能把所有能够找到的设计模式的例子一一列举,只是找了一些容易发现的例子。实际上也没有必要,因为只要对一个设计模式有足够的理解,对于它的具体应用而
言,倒是一件不是很困难的事情。
Command模式
在设计一般用途的软件的时候,在C或者C++语言中,用的很多的一个技巧就是回调函数(Callback),所谓的回调函数,意指先在系统的某个地方对函
数进行注册,让系统知道这个函数的存在,然后在以后,当某个事件发生时,再调用这个函数对事件进行响应。在C或者C++中,实现的回调函数方法是使用函数
指针。但是在Java中,并不支持指针,因而就有了Command模式,这一回调机制的面向对象版本。
Command模式用来封装一个命令/请求,简单的说,一个Command对象中包含了待执行的一个动作(语句)序列,以执行特定的任务。当然,并不是随
便怎么样的语句序列都可以构成一个Command对象的,按照Command模式的设计,Command对象和它的调用者Incvoker之间应该具有接
口约定的。也就是说,Invoker得到Command对象的引用,并调用其中定义好的方法,而当Command对象改变(或者是对象本身代码改变,或者
干脆完全另外的一个Command对象)之后,Invoker中的代码可以不用更改。这样,通过封装请求,可以把任务和任务的实现加以分离。
图二:Command模式的类图
而对于请求的处理又有两种不同的方法,一种是Command只充当代理,将请求转发给某个接受者对象,还有一种是Command对象自己处理完所有的请求操作。当然,这只是两个极端,更多的情况是Command完成一部分的工作,而另外的一部分这则交给接受者对象来处理。
在新的JDK的代理事件模型中,就可以看作是这样的一个Command模式。在那个模型中,一个事件监听者类EventListener监听某个事件,并
根据接口定义,实现特定的操作。比如,当用Document对象的addDocumentListener(DocumentListener
listener)
方法注册了一个DocumentListener后,以后如果在Document对象中发生文本插入的事件,DocumentListener中实现的
insertUpdate(DocumentEvent
e)方法就会被调用,如果发生文本删除事件,removeUpdate(DocumentEvent
e)方法就会被调用。怎么样,想想看,这是不是一个Command模式的应用呢?
然而,最经典的Command模式的应用,莫过于Swing中的Action接口。Action实际上继承的是ActionListener,也就是说,
它也是一个事件监听者(EventListener)。但是Action作为一种ActionListener的扩展机制,提供了更多的功能。它可以在其
中包含对这个Action动作的一个或者多个文字的或图标的描叙,它提供了Enable/Disable的功能许可性标志。并且,一个Action对象可
以被多个Invoker,比如实现相同功能的按钮,菜单,快捷方式所共享。而这些Invoker都知道如何加入一个Action,并充分利用它所提供的扩
展机制。可以说,在这儿Action更像一个对象了,因为它不仅仅提供了对方法的实现,更提供了对方法的描叙和控制。可以方便的描叙任何的事务,这更是面
向对象方法的威力所在。
下面我们看一个Command模式的应用的例子。假设要实现这样的一个任务:Task
Schedule。也就是说,我想对多个任务进行安排,比如扫描磁盘,我希望它每1个小时进行一次,而备份数据,我希望它半个小时进行一次,等等等等。但
是,我并不希望作为TaskSchedule的类知道各个任务的细节内容,TaskSchedule应该只是知道Task本身,而对具体的实现任务的细节
并不理会。因而在这儿,我们就需要对TaskSchedule和Task进行解耦,将任务和具体的实现分离出来,这不正是Command模式的用武之地
吗?
图三:Command模式的应用例子
程序清单:
//抽象的Task接口,作为回调的Command模式的主体
public interface Task {
public void taskPerform();
}
//具体的实现了Task接口的子类,实现特定的操作。
public class BackupTask implements Task{
public void taskPerform(){
System.out.println("Backup Task has been performed");
}
}
//具体的实现了Task接口的子类,实现特定的操作。
public class ScanDiskTask implements Task{
public void taskPerform(){
System.out.println("ScanDisk Task has been performed");
}
}
//一个封装了Task的一个封装类,提供了一些与Task相关的内容,也可以把这些内容
//这儿不过为了突出Command模式而把它单另出来,实际上可以和Task合并。
public class TaskEntry {
private Task task;
private long timeInterval;
private long timeLastDone;
public Task getTask() {
return task;
}
public void setTask(Task task) {
this.task = task;
}
public void setTimeInterval(long timeInterval) {
this.timeInterval = timeInterval;
}
public long getTimeInterval() {
return timeInterval;
}
public long getTimeLastDone() {
return timeLastDone;
}
public void setTimeLastDone(long timeLastDone) {
this.timeLastDone = timeLastDone;
}
public TaskEntry(Task task,long timeInteral){
this.task=task;
this.timeInterval =timeInteral;
}
}
//调度管理Task的类,继承Thread只是为了调用其sleep()方法,
//实际上,如果真的作Task调度的话,每个Task显然应该用单独的Thread来实现。
public class TaskSchedule extends java.lang.Thread {
private java.util.Vector taskList=new java.util.Vector();
private long sleeptime=10000000000l;//最短睡眠时间
public void addTask(TaskEntry taskEntry){
taskList.add(taskEntry);
taskEntry.setTimeLastDone(System.currentTimeMillis());
if (sleeptime>taskEntry.getTimeInterval())
sleeptime=taskEntry.getTimeInterval();
}
//执行任务调度
public void schedulePermorm(){
try{
sleep(sleeptime);
Enumeration e = taskList.elements();
while (e.hasMoreElements()) {
TaskEntry te = (TaskEntry) e.nextElement();
if (te.getTimeInterval() + te.getTimeLastDone() <
System.currentTimeMillis()) {
te.getTask().taskPerform();
te.setTimeLastDone(System.currentTimeMillis());
}
}
}catch (Exception e1){
e1.printStackTrace();
}
}
public static void main (String args[]){
TaskSchedule schedule=new TaskSchedule();
TaskEntry taks1=new TaskEntry(new ScanDiskTask(),10000);
TaskEntry taks2=new TaskEntry(new BackupTask(),3000);
schedule.addTask(taks1);
schedule.addTask(taks2);
while (true){
schedule.schedulePermorm();
}
}
}
|
程序本身其实没有多大的意义,因而,程序在编码的时候也只是用的最简单的方法来实现的,如果要做一个真正的TaskSchedule的话,这个程序除了结构上的,其它没有什么好值得参考的了。
回页首
AbstractFactory 和 FactoryMethod
基本上来说,AbstractFacotry模式和FactoryMethod模式所作的事情是一样的,都是用来创建与具体程序代码无关的对象,只是面对
的对象层次不一样,AbstractFactory创建一系列的对象组,这些对象彼此相关。而FactoryMethod往往只是创建单个的对象。
再开始这两个模式之前,有必要先陈叙一个在设计模式,或者说在整个面向对象设计领域所遵循的一个设计原则:针对接口编程,而不是针对具体的实现。这个思想
可以说是设计模式的基石之一。现在的很多对象模型,比如EJB,COM+等等,无不是遵照这个基本原则来设计的。针对接口编程的好处有很多,通过接口来定
义对象的抽象功能,方便实现多态和继承;通过接口来指定对象调用之间的契约,有助于协调对象之间的关系;通过接口来划分对象的职责,有助于寻找对象,等
等。
AbstractFactory和FactoryMethod,还有其他的一些创建型的设计模式,都是为了实现这个目的而设计出来的。它们创建一个个符合
接口规范的对象/对象组,使得用同一个Factory创建出来的对象/对象组可以相互替换。这种可替换性就称为多态,是面向对象的核心思想之一。而多态,
是通过动态绑定来实现的。
图四:AbstractFactory模式的类图
客户程序使用具体的AbstractFacotry对象(ConcreteFactoryX)调用CreateProductX()方法,生成具体的
ConcreteProductX。每个AbstractFactory所能生成的对象,组成一个系列的对象组,他们可能是相互相关的,紧耦合的。应为各
个AbstractFactory对象所能够生成的对象组都遵循一组相同的接口(AbstractProductX),因而当程序是针对接口进行编程的时
候,这些实现方法各不相同的对象组却可以相互的替换。
实际上,客户程序本身并不关心,也不知道具体使用的是那些产品对象。它甚至能够不理会到底是哪个AbstractFactory对象被创建。在这种情况下,你可能会问,那么一个AbstractFactory又该如何生成呢?
这时候,就该用该FactoryMethod模式了。
前面有说过,AbstractFactory着重于创建一系列相关的对象,而这些对象与具体的AbstractFactory相关。而
FactoryMethod则着重于创建单个的对象,这个对象决定于一个参数或者一个外部的环境变量的值;或者,在一个抽象类中定义一个抽象的工厂方法
(也成为虚拟构造器),然后再实现的子类中返回具体的产品对象。
FactoryMethod可以借助一个参数或者一个外部的标志来判断该具体生成的哪一个子类的实例。比如对于不同的具体情况,需要有不同的
AbstractFactory来生成相应的对象组。这时候,FactoryMethod通常作为一个AbstractFactory对象的静态方法出
现,使得其能够在具体的对象被创建之前就能够被调用。
在JAVA中,应用这两个模式的地方实在太多,下面我们来看一个在JAXP中这两个模式的应用。JAXP是用来处理XML文档的一个API。我们都知道
XML文件的一个特点就是其平台无关,流通性能好。因而往往也需要处理他们的程序具有更好的平台无关性。Java语言是一个比较好的平台无关语言,可以作
为一个选择,但是对XML进行解析的解析器确有很多。有时候需要在不同的解析器之间进行切换,这时候,JAXP的良好设计就能够体现出来了。它能够允许在
不同解析器之间竟进行切换的时候,不用更改程序的代码。
我们就拿JAXP中的DOM解析器来作为例子,来例示AbstractFactory和FactoryMethod的用法。
图五:DOM中工厂模式的应用
上图中为了方便起见,只画出了抽象类和接口,DocumentBuilderFactory和DocumentBuilder都是抽象类。
DocumentBuilderFactory的静态方法newInstance()方法根据一个外部的环境变量
javax.xml.parsers.DocumentBuilderFactory的值来确定具体生成DocumentBuilderFactory的
哪一个子类。这儿的newInstance()是一个工厂方法。当DocumentBuilderFactory被创建后,可以调用其
newDocumentBuilder()来创建具体一个DocumentBuilder的子类。然后再由DocumentBuilder来生成
Document等DOM对象。
下面是创建一个DOM对象的代码片段:
//第一步:创建一个DocumentBuilderFactory。
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
//第二步:创建一个DocumentBuilder
DocumentBuilder db = dbf.newDocumentBuilder();
//第三步:解析XML文件得到一个Document对象
Document doc = db.parse(new File(filename));
|
在这儿,DocumentBuilder,Document,Node等等对象所组成的一个产品组,是和具体的DocumentBuilderFactory相关的。这也就是AbstractFactory模式的含义所在。
当然,FactoryMethod模式应用的很广。这是一个具体的例子,但他不应该限制我们的思路,FactoryMethod和AbstractFactory是解决面向对象设计中一个基本原则--面向接口编程的主要方法。
回页首
Singleton模式
Singleton模式要解决的是对象的唯一性问题。由Singleton模式创建的对象在整个的应用程序的范围内,只允许有一个对象的实例存在。这样的情况在Java程序设计的过程中其实并不少见,比如处理JDBC请求的连接池(Connection
Pool),再比如一个全局的注册表(Register),等等,这都需要使用到Singleton,单件模式。
在Java中,最简单的实现Singleton模式的方法是使用static修饰符,static可以用在内部类上,也可以用在方法和属性上,当一个类需
要被创建成Singleton时,可以把它所有的成员都定义成static,然后再用final和private来修饰其构造函数,使其不能够被创建和重
载。这在程序语法上保证了只会有一个对象实例被创建。比如java.util.Math就是这样的一个类。
而Singleton模式所作的显然要比上面介绍的解决方法要复杂一些,也更为安全一些。它基本的思路也还是使用static变量,但是它用一个类来封装
这个static变量,并拦截对象创建方法,保证只有一个对象实例被创建,这儿的关键在于使用一个private或者protected的构造函数,而且
你必须提供这样的一个构造函数,否则编译器会自动的为你创建一个public的构造函数,这就达不到我们想要的目的了。
public class Singleton {
//保存唯一实例的static变量
static private Singleton _instance = null;
/*为了防止对象被创建,可以为构造函数加上private修饰符,但是这同样也防止了子类的对象被创建,因而,可以选用protected修饰符来替代private。*/
protected Singleton() {
// ...
}
//static方法用来创建/访问唯一的对象实例,这儿可以对对象的创建进行控制,使得可//以很容易的实现只允许指定个数的对象存在的泛化的Singleton模式。
static public Singleton instance() {
if(null == _instance) {
_instance = new Singleton();
}
return _instance;
}
// ...
}
|
对象创建的方法,除了使用构造函数之外,还可以使用Object对象的clone()方法,因而在Singleton中也要注意这一点。如果
Singleton类直接继承于Object,因为继承于Object的clone()方法仍保留有其protected修饰,因而不能够被其他外部类所
调用,所以可以不用管它,但是如果Singleton继承于一个其他的类,而这个类又有重载clone()方法,这时就需要在Singleton中再重载
clone()方法,并在其中抛出CloneNotSupportedException,这样就可以避免多个Singleton的实例被创建了。
在JDK1.2以前的版本中使用Singleton模式的时候有一些需要额外注意的地方,因为Singleton类并没有被任何其他的对象所引用,所以这
个类在创建后一段时间会被unload,Singleton类的静态方法就会出现问题,这是由于Java中垃圾收集机制造成的。解决的方法也很容易,只需
要为其创建一个引用就行了。而在JDK1.2以后的版本中,Sun重新定义了Java规范,改正了其垃圾收集机制中的一些问题,这个问题也就不复存在了,
这儿指出只是为了提起大家的主意。
回页首
小结:
Command模式用来封装请求,也描叙了一致性的发送请求的接口,允许你配置客户端以处理不同的请求,为程序增添了更大的灵活性。Singleton模
式为提供对象的单一入口提供了帮助。AbstractFactory和FactoryMethod模式在功能上比较类似,都是用来处理对象的创建的,但应
用在不同的层面上。在创建型模式中,还有Builder模式和Prototype模式,这儿不打算详细的讨论了,简单的说,Builder模式用来处理对
象创建的细节。在两个工厂模式中都没有涉及到对象创建的具体细节,都是通过接口来返回一个给定类型的对象。而Builder模式则需要对创建一个给定类型
对象的过程进行建模。这对创建复杂对象时很有用,使得创建对象的算法独立于对象各个组成部分的创建。而Prototype模式使用原型机制,通过创建简单
原型的拷贝来创建对象。
分享到:
相关推荐
Java常用设计模式(SingleTon、FactoryMethod、AbstractFactory)
设计模式之Factory 设计模式之Prototype(原型) 设计模式之Builder 设计模式之Singleton(单态) 结构模式: 设计模式之Facade(外观) 设计模式之Proxy(代理) 设计模式之Adapter(适配器) 设计模式之Composite(组合) 设计...
设计模式-Singleton与Factory
用Java实现23种设计模式 1. 创建型模式 工厂模式(Factory Pattern) 抽象工厂模式(Abstract Factory Pattern) 单例模式(Singleton Pattern) 建造者模式(Builder Pattern) 原型模式(Prototype Pattern)...
6、抽象工厂模式ABSTRACT FACTORY PATTERN 7、门面模式FACADE PATTERN 8、适配器模式ADAPTER PATTERN 9、模板方法模式TEMPLATE METHOD PATTERN 10、建造者模式BUILDER PATTERN 11、桥梁模式BRIDGE PATTERN 12、...
抽象工厂模式(Abstract Factory Pattern) 单例模式(Singleton Pattern) 建造者模式(Builder Pattern) 原型模式(Prototype Pattern) 2 结构型模式 这些设计模式关注类和对象的组合。继承的概念被用来组合接口...
java的设计模式大体上分为三大类: 2 设计模式遵循的原则有6个: 2 1. 工厂模式(Factory Method) 2 2. 抽象工厂模式(Abstract Factory) 3 3. 单例模式(Singleton) 4 4.建造者模式(Builder) 4 5. 原型模式...
《java设计模式(第2版)》通过一个完整的java项目对经典著作design patterns一书介绍的23种设计模式进行了深入分析与讲解,实践性强,却又不失对模式本质的探讨。本书创造性地将这些模式分为5大类别,以充分展现各个...
主要介绍了Java设计模式之单态模式(Singleton模式)介绍,本文讲解了如何使用单例模式、使用单例模式注意事项等内容,需要的朋友可以参考下
C++设计模式课件12_Singleton_单件模式.pdf
设计模式精解-GoF 23 种设计模式解析附 C++实现源码 目 录 引 言 0.1 设计模式解析(总序) 0.2 设计模式解析后记 0.3 与作者联系 1 创建型模式 1.1 Factory模式 1.2 AbstactFactory模式 1.3 Singleton...
java singleton 不解释不解释不解释不解释
简单的单例模式举例Singleton 分为恶汉式 懒汉式
第1章到第 11章陆续介绍的设计 5b4 式为Strategy、Observer、Decorator、Abstract Factory、Factory Method、Singleton,Command、Adapter、Facade、TemplateMethod、Iterator、 Composite、State、Proxy。...
适合Java设计模式期末考试选择题复习,形式如下: 10.(单选题)Facade(外观)模式的意图是 正确答案: A A.希望简化现有系统的使用方法,你需要定义自己的接口 B.将一个无法控制的现有对象与一个特定借口相匹配。 C....
java Singleton单例模式 java Singleton单例模式
AbstractFactory ( 抽象工厂 ) FactoryMethod ( 工厂方法 ) Singleton ( 单态模式 ) Builder ( 建造者模式 ) Prototype ( 原型模式 ) Adapter ( 适配器模式 ) Bridge ( 桥接模式 ) Composite ( 组合模式 ) ...
- 抽象工厂模式(Abstract Factory) - 单例模式(Singleton) - 建造者模式(Builder) - 原型模式(Prototype) - 代理模式(Proxy) - 适配器模式(Adapter) - 装饰器模式(Decorator) - 桥接模式(Bridge) - 组合...
第1章到第11章陆续介绍的设计模式为Strategy、Observer、Decorator、AbstractFactory、FactoryMethod、Singleton,Command、Adapter、Facade、TemplateMethod、Iterator、Composite、State、Proxy。最后三章比较特别...
创建型模式(Creational Pattern) 是对类的实例化过程的抽象化。一些系统在创建对象时,需要动态地决定怎样创建对象,创建哪些对象,以及如何组合和表示这些对象。创建型模式描述了怎样构造和封装这些动态的决定...