论坛登陆 用户名:  密 码:
设为首页  加入收藏
08年北京名校秋季招生
名牌院校免试入学宽进严出,突破考分限制,名校与你零距离,以下院校按报名先后顺序录取,24小时网上报名覆盖全国
  您现在的位置: 中国教育招生在线 >> IT >> JAVA认证 >> IT正文
在Java中应用设计模式-Factory Method
 作者:佚名     2007-3-14 16:10:51        来源:不详  浏览次数:

 

 

 

 

 

 

 

 

在设计模式中,Factory Method也是比较简单的一个,但应用非常广泛,EJB,RMI,COM,CORBA,Swing中都可以看到此模式的影子,它是最重要的模式之一.在很多地方我们都会看到xxxFactory这样命名的类,那么,什么是Factory Method,为什么要用这个模式,如何用Java语言来实现该模式,这就是本文想要带给大家的内容.

基本概念

Factory Method是一种创建性模式,它定义了一个创建对象的接口,但是却让子类来决定具体实例化哪一个类.当一个类无法预料要创建哪种类的对象或是一个类需要由子类来指定创建的对象时我们就需要用到Factory Method 模式了.简单说来,Factory Method可以根据不同的条件产生不同的实例,当然这些不同的实例通常是属于相同的类型,具有共同的父类.Factory Method把创建这些实例的具体过程封装起来了,简化了客户端的应用,也改善了程序的扩展性,使得将来可以做最小的改动就可以加入新的待创建的类. 通常我们将Factory Method作为一种标准的创建对象的方法,当发现需要更多的灵活性的时候,就开始考虑向其它创建型模式转化

简单分析

图1是Factory Method 模式的结构图,这里提供了一些术语,让我们可以进行更方便的描述:

Product: 需要创建的产品的抽象类.

ConcreteProduct: Product的子类,一系列具体的产品.

Creator: 抽象创建器接口,声明返回Product类型对象的Factory Method.

ConcreteCreator: 具体的创建器,重写Creator中的Factory Method,返回ConcreteProduct类型的实例.

图1: Factory Method 模式结构

由此可以清楚的看出这样的平行对应关系: Product <====> Creator ; ConreteProduct <====> ConreteCreator

抽象产品对应抽象创建器,具体产品对应具体创建器.这样做的好处是什么呢?为什么我们不直接用具体的产品和具体的创建器完成需求呢?实际上我们也可以这样做.但通过Factory Method模式来完成,客户(client)只需引用抽象的Product和Creater,对具体的ConcreteProduct和 ConcreteCreator可以毫不关心,这样做我们可以获得额外的好处:

首先客户端可以统一从抽象创建器获取产生的实例, Creator的作用将client和产品创建过程分离开来,客户不用操心返回的是那一个具体的产品,也不用关心这些产品是如何创建的.同时, ConcreteProduct也被隐藏在Product后面,ConreteProduct继承了Product的所有属性,并实现了Product中定义的抽象方法,按照Java中的对象造型(cast)原则,通过ConcreteCreator产生的ConcreteProduct可以自动的上溯造型成Product.这样一来,实质内容不同的ConcreteProduct就可以在形式上统一为Product,通过Creator提供给 client来访问.

其次,当我们添加一个新的ConcreteCreator时,由于Creator所提供的接口不变,客户端程序不会有丝毫的改动,不会带来动一发而牵全身的灾难, 这就是良好封装性的体现.但如果直接用ConcreteProduct和ConcreteCreator两个类是无论如何也做不到这点的. 优良的面向对象设计鼓励使用封装(encapsulation)和委托(delegation),而Factory Method模式就是使用了封装和委托的典型例子,这里封装是通过抽象创建器Creator来体现的,而委托则是通过抽象创建器把创建对象的责任完全交给具体创建器ConcreteCreator来体现的.

现在,请再回头看看基本概念中的那段话,开始也许觉得生涩难懂,现在是不是已经明朗化了很多.

下面让我们看看在 Java 中如何实现Factory Method模式,进一步加深对它的认识.

具体实施

先说明一点,用Factory Method模式创建对象并不一定会让我们的代码更短,实事上往往更长,我们也使用了更多的类,真正的目的在于这样可以灵活的,有弹性的创建不确定的对象.而且,代码的可重用性提高了,客户端的应用简化了,客户程序的代码会大大减少,变的更具可读性.

标准实现: 这里我采用Bruce Eckel 用来描述OO思想的经典例子 Shape.这样大家会比较熟悉一些.我完全按照图1中所定义的结构写了下面的一段演示代码.这段代码的作用是创建不同的Shape实例,每个实例完成两个操作:draw和erase.具体的创建过程委托給ShapeFactory来完成.

1.a 首先定义一个抽象类Shape,定义两个抽象的方法.

abstract class Shape {

// 勾画shape

public abstract void draw();

// 擦去 shape

public abstract void erase();

public String name;

public Shape(String aName){

   name = aName;

}

}

1.b 定义 Shape的两个子类: Circle, Square,实现Shape中定义的抽象方法

// 圆形子类

class Circle extends Shape {

public void draw() {

  System.out.println("It will draw a circle.");

}

public void erase() {

  System.out.println("It will erase a circle.");

}

// 构造函数

public Circle(String aName){

   super(aName);

}

}


// 方形子类

class Square extends Shape {

public void draw() {

  System.out.println("It will draw a square.");

}

public void erase() {

  System.out.println("It will erase a square.");

}

// 构造函数

public Square(String aName){

   super(aName);

}

}


1.c 定义抽象的创建器,anOperation调用factoryMethod创建一个对象,并对该对象进行一系列操作.
abstract class ShapeFactory { 

protected abstract Shape factoryMethod(String aName);

// 在anOperation中定义Shape的一系列行为

public void anOperation(String aName){

  Shape s = factoryMethod(aName);

   System.out.println("The current shape is: " + s.name);

  s.draw();

 s.erase();

}

}


1.d 定义与circle和square相对应的两个具体创建器CircleFactory,SquareFactory,实现父类的methodFactory方法

// 定义返回 circle 实例的 CircleFactory
class CircleFactory extends ShapeFactory {

// 重载factoryMethod方法,返回Circle对象

protected Shape factoryMethod(String aName) {

  return new Circle(aName + " (created by CircleFactory)");

}

}


// 定义返回 Square 实例的 SquareFactory

class SquareFactory extends ShapeFactory {

// 重载factoryMethod方法,返回Square对象

protected Shape factoryMethod(String aName) {

  return new Square(aName + " (created by SquareFactory)");

}

}


1.e 测试类:请注意这个客户端程序多么简洁,既没有罗嗦的条件判断语句,也无需关心ConcreteProduct和ConcreteCreator的细节 (因为这里我用anOperation封装了Product里的两个方法,所以连Product的影子也没看见,当然把Product里方法的具体调用放到客户程序中也是不错的).

class Main {

public static void main(String[] args){

ShapeFactory sf1 = new SquareFactory();

ShapeFactory sf2 = new CircleFactory();

sf1.anOperation("Shape one");

sf2.anOperation("Shape two");

}

}

运行结果如下:

The current shape is: Shape one (created by SquareFactory)

It will draw a square.

It will erase a square.

The current shape is: Shape two (created by CircleFactory)

It will draw a circle.

It will erase a circle.

参数化的Factory Method: 这种方式依靠指定的参数作为标志来创建对应的实例,这是很常见的一种办法.比如JFC中的BorderFactory就是个很不错的例子. 以下的这个例子是用字符串作为标记来进行判断的,如果参数的类型也不一样,那就可以用到过载函数来解决这个问题,定义一系列参数和方法体不同的同名函数, 这里java.util.Calendar.getInstance()又是个极好的例子.参数化的创建方式克服了Factory Method模式一个最显著的缺陷,就是当具体产品比较多时,我们不得不也建立一系列与之对应的具体构造器. 但是在客户端我们必须指定参数来决定要创建哪一个类.

2.a 我们在第一种方法的基础上进行修改,首先自定义一个的异常,这样当传入不正确的参数时可以得到更明显的报错信息.
class NoThisShape extends Exception {

public NoThisShape(String aName) {

  super(aName);

}

}


2.b去掉了ShapeFactory的两个子类,改为由ShapeFactory直接负责实例的创建. ShapeFactory自己变成一个具体的创建器,直接用参数化的方法实现factoryMethod返回多种对象.

abstract class ShapeFactory { 

private static Shape s;

private ShapeFactory() {}

static Shape factoryMethod(String aName, String aType) throws NoThisShape{

  if (aType.compareTo("square")==0)

    return new Square(aName);

   else if (aType.compareTo("circle")==0)

    return new Circle(aName);

  else throw new NoThisShape(aType); 

}

// 在anOperation中定义Shape的一系列行为

static void anOperation(String aName, String aType) throws NoThisShape{

  s = factoryMethod(aName, aType);

  System.out.println("The current shape is: " + s.name);

  s.draw();

 s.erase();

}

}

2.c 测试类:这里客户端必须指定参数来决定具体创建哪个类.这个例子里的anOperation是静态函数,可以直接引用.

class Main {

public static void main(String[] args) throws NoThisShape{

ShapeFactory.anOperation("Shape one","circle");

ShapeFactory.anOperation("Shape two","square");

ShapeFactory.anOperation("Shape three", "delta");

}

}

运行结果如下:

The current shape is: Shape one

It will draw a circle.

It will erase a circle.

The current shape is: Shape two

It will draw a square.

It will erase a square.

Exception in thread "main" NoThisShape: delta

at ShapeFactory.factoryMethod(ShapeFactory.java:10)

at ShapeFactory.anOperation(ShapeFactory.java:15)

at Main.main(Main.java:5)

动态装载机制:

有的时候我们会把ConcreteProduct的实例传给创建器作为参数,这种情况下,如果在创建器里完成创建过程,就必须判断参数的具体类型(用instanceof),然后才能产生相应的实例,那么比较好的做法是利用Java的动态装载机制来完成这件事.比如:

我们得到一个Shape的子类s,但不知道具体是那个子类,就可以利用Class类自带的方法newInstance()得到实例 return (Shape)s.getClass().newInstance();


责任编辑:lss
  相关新闻
JSFToolbox--用Dreamweaver开发JSF
Java模板引擎Velocity基本语法
【JAVA基础】JDBC连接DB2数据库详解
为什么要学习Java,成为Java程序员
Java基础-漫谈EJB在Java中的应用
漫谈EJB在Java中的应用(二)
J2EE技术-漫谈EJB在Java中的应用
JAVA测试:使用JMock来实现孤立测试
看看如何在Struts应用中施展AJAX魔法
通过java.net.Socket类抓取网页内容
为什么要学习Java,成为Java程序员
Java基础-漫谈EJB在Java中的应用
漫谈EJB在Java中的应用(二)
运用Jakarta Struts的七大实战心法
通过Java Swing 看透MVC设计模式
开发保留标准浏览器功能的AJAX应用程序
Web框架趣谈之Java Web 框架的甜点
Web2.0时代的核心应用:Ajax简介
精华:AJAX开发简略(第一部分)
Thinking in AJAX(三)——AJAX框架汇总
  评论
现在有100人对本文发表评论
查看所有评论
 
推  荐
 
100本成功必读热销书
热门招生
  北京文理研修学院   前进大学
  北京明园大学   北京建设大学
  北京邮电大学世纪学院   北方工商管理学院
  联想软件定向委培班   香港数码动画学院
  青年企业管理研修学院   北京华夏管理学院
热门培训
网络化办公专家培训认证 电子科技大学软件学院
软件测试工程师培训认证 北大青鸟十大授权培训
IT硬件工程师培训认证班 北京环球雅思荷兰预科
JAVA开发工程师培训 潜能时代IT服务管理培训
网络信息化工程师培训 清华大学继续教育学院
论坛精选
 
有些细节是男人也该注意的风度!最容易读错的字
某强人手机里保存的30条短信 中国十大高薪职业
最感人的十大韩剧经典台词 嫁给工程师的N个理由
爆强!只有一句话的鬼故事 转贴教你如何做妖精
 女人一定要記住的話 女人最好别嫁给最爱的男人
城市联盟
 大连 上海 天津 广州 西安 深圳  天津  青岛  大连  福州  沈阳  青海  连云港  南京  吉林  厦门  威海  辽宁  呼和浩特
Copyright © 2006   www.edu999.com   All rights reserved. 中国教育招生在线  版权所有
北京市通信管理局[2004]字第552号函    京ICP证040442号