2008年9月28日星期日

Java 设计架构(四)


第二节 工厂创建模式与单例模式

l        简单工厂(Simple Factory)模式:又称静态工厂方法模式(Static Factory Method Pattern)

l        工厂方法(Factory Method)模式:又称多态性工厂(Polymorphic Factory)模式或虚拟构造子(Virtual Constructor)模式

l        抽象工厂(Abstract Factory)模式:又称工具箱(Toolkit)模式

下面以简单工厂设计模式为例说明代码模式在Java编程中的应用。

从上图可以看出,简单工厂模式涉及到工厂角色,抽象产品角色以及具体产品角色等三个角色:

l        工厂类(Creator)角色:担任这个角色的是工厂方法模式的核心,含有与应用紧密相关的商业逻辑,工厂类在客户端的直接调用下创建产品对象,它往往由一个具体Java类来实现

l        抽象产品(Product)角色:担任这个角色的类是由工厂方法模式所创建的对象的父类,或它们共同拥有接口。抽象产品角色可以用一个Java接口或者Java抽象类实现

l        具体产品(Concrete Product)角色:工厂方法模式所创建的任何对象都是这个角色的实例,具体产品角色由一个具体Java类实现

例如我们要在程序中产生两个对象:一个圆形与一个方形,建立时要同时设定它们的中心位置,然后它们会负责画出自己。我们可以设计一个 Shape Factory工厂类,负责创建指定的对象,调用者只管指定对象名称与中心位置,而不管这些对象如何产生,对象生成后的中心位置设定被隐藏于 Shape Factory工厂类中。

                           

    如上图所示的,Main代表了客户的角色,它只依赖于表层业务调用,而不关心特定的实例,实例的细节由Shape Factory完成,我们以一个简单的程序来实现上面这个UML类图:

  • IShape接口
  public interface IShape {    //建立IShape接口
    public void setCenter(int x, int y);  
    public void draw();        //定义一个接口方法
}
  • Circle类实现IShape接口
  public class Circle implements IShape {
    private int x;
    private int y;
public void setCenter(int x, int y) {//实现IShape接口setCenter方法
        this.x = x;
        this.y = y;
    }
  public void draw() {  //实现IShape接口draw()方法
        System.out.println("Circle center at ("
            + x + ", " + y + ")");
    }
}
  • Square类实现IShape接口
  public class Square implements IShape {
    private int x;
    private int y;
    public void setCenter(int x, int y) { //实现IShape接口setCenter方法
        this.x = x;
        this.y = y;
    }
    public void draw() {//实现IShape接口draw()方法
        System.out.println("Square center at ("
            + x + ", " + y + ")");
    }
}
  • ShapeFactory工厂类
  public class ShapeFactory {  
    public static IShape createShapeAt(String name, int x, int y) {
        try {
            IShape shape
               = (IShape) Clas.forName(name).newInstance();//使用Java反射技术获得名为name产品实例,并让IShape接口持有此实例对象
            shape.setCenter(x,y);
            return shape;
        }
        catch(Exception e) {
            System.out.println(
                  "Sorry! No such class defined!"); return null;
        }
    }
}
  • Main类
  public class Main {
    public static void main(String args[]) {
        // 产生一个圆形并且显示它
        ShapeFactory.createShapeAt("Circle", 10, 10).draw();
       System.Out.println();
      // 产生一个方形并显示它
        ShapeFactory.createShapeAt("Square", 20, 25).draw();
    }
}

    客户只要面对Factory,客户依赖于产品的调用介面,产品的具体实例是可以与客户隔开的,它们也是可以互换的。简单工厂模式是工厂方法模式与抽象工厂模式的一种特殊情况,关于它们的实现可以参考相关资料。

单例模式确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。单例模式在系统设计时使用非常广泛,常用的资源管理器一般使用此模式,在一些线程安全的情况下使用也比较多,如数据库连接池访问接口,属性文件管理等。

  

以上面的UML图为例来说明单例模式的使用方法,我们可以在第一次需要实例时再创建对象,也就是采用所谓的Lazy Initialization懒汉式:
public class Singleton {
    private static Singleton instance = null;
    private Singleton() {
        // .......
    }

  public static Singleton getInstance() { //获得系统唯一实例的静态方法
        if (instance == null)  instance = new Singleton();
      return instance;
    }
// ...... 其它代码
}
    上面的代码适用于单线程的程序,在多线程的程序下,以下的写法在多个线程的竞争资源下,将仍有可能产生两个以上的对象,存在线程安全性问题,例如下面的情况:

Thread1: if(instance == null) // true
Thread2: if(instance == null) // true
Thread1: instance = new Singleton(); // 产生一个实例
Thread2: instance = new Singleton(); // 又产生一个实例
Thread1: return instance; // 返回一个实例
Thread2: return instance; // 又返回一个实例
    在多线程环境下,为了了避免资源同时竞争而导致如上产生多个实例的情况,我们加上同步机制:

  public class Singleton {
    private static Singleton instance = null;
    private Singleton(){}
    synchronized static public Singleton getInstance() {//为获得系统唯一实例的静态方法加上线程同步机制,保证此方法同时只能被一线程调用
        if (instance == null) instance = new Singleton();
        return instance;
    } }

第三节 使用工厂模式实现DAO

由于J2EE模式众多,篇幅有限,这里只概要介绍其中的一种使用工厂模式实现数据访问对象。
    使用数据访问对象(DAO)来抽象和封装所有对数据源的访问。 DAO管理着与数据源的连接以便于检索和存储数据,DAO实现了用来操作数据源的访问机制。依赖于DAO的业务组件为其客户端使用DAO提供了更简单的接口,DAO完全向客户端隐藏了数据源实现细节。由于当低层数据源实现变化时,DAO向客户端提供的接口不会变化,所以该模式允许DAO调整到不同的存储模式,而不会影响其客户端或业务组件。重要的是,DAO充当组件和数据源之间的适配器。
    当低层存储随着实现的变化而变化时,策略可以通过使用抽象工厂模式而实现。抽象工厂可以基于工厂方法实现而创建,并可使用工厂方法实现,该策略提供一个 DAO的抽象工厂对象,其中该对象可以构造多种类型的具体的DAO工厂,每个工厂支持一种不同类型的持久性存储实现。一旦你获取某特定实现的具体DAO工厂,你可以使用它来生成该实现中所支持和实现的DAO,如下面类图所示。

    例如在一个项目中有四张表,分别是用户信息表(user Info),论坛表(Forum),主题表(Topic),回复表(Reply)。先创建与数据库表相对应组件Java Bean,然后为每一个数据库表配置一个数据库操作接口。如下所示:

UserInfo访问对象接口

package com.webclass.dao;

public interface UserInfoDao {

    //设置帖子属性时,用户的属性

      public void upUserState(String state,String usrName);

     // 查询用户是否存在判断(返回整个用户)

      public UserInfo selectUser(String usrName);

      //设置用户权限

   public void upUserRole(int i ,String usrRole,int usrId);

  //修改用户信息

      public boolean editUser(UserInfo user);

……………………………等操作逻辑方法

Forum访问对象接口

package com.webclass.dao;

public interface ForumDao {

…………………操作逻辑方法

}

Reply访问对象接口

package com.webclass.dao;

public interface ReplyDao {

………………操作逻辑方法

}

Topic访问对象接口

package com.webclass.dao;

public interface TopicDao {

………………操作逻辑方法

}  

    接下来就是写具体类实现以上其接口,而接口方法不依赖于类的具体实现。数据库的访问对象已经创建好之后,应用程序将调用这些对象进行数据库操作,如何调用这些对象呢?我们使用一个工厂类来专门负责这些对象的创建工作:

public class DBFactory {

    public static ForumDao dBForum=null;

    public static ReplyDao dBReply=null;

    public static TopicDao dBTopic=null;

    public static UserInfoDao dBUserInfo=null;

    synchronized public static ForumDao getDBForum(){

       if(dBForum==null) dBForum=new DBForum();

       return dBForum;

    }  

    synchronized public static ReplyDao getDBReply(){

       if(dBReply==null) dBReply=new DBReply();

       return dBReply;

    }

    synchronized public static TopicDao getDBTopic(){

       if(dBTopic==null) dBTopic=new DBTopic();

       return dBTopic;

    }

    synchronized public static UserInfoDao getDBUserInfo(){

       if(dBUserInfo==null) dBUserInfo=new DBUserInfo();

       return dBUserInfo;

    }

}

这样应用程序的上层组件就可以直接调用DBFactory工厂对象所持有的静态对象来进来数据库操作了,更重要的是当底层数据库访问操作发生改变时只需修改访问对象,而不会波及到上层组件,使用软件健壮性和灵活性大大提高。

总结

    即使是利用当今最先进的软件平台J2EE,开发企业应用程序仍然是一个难题,因为软件的复杂性和脆弱性无法回避。J2EE通过J2EE API提供了技术与服务的高层抽象,使企业开发得到了一定的简化。但是,仅仅知道J2EE API是不够的。要设计良好的体系结构,得到高质量的应用程序,要知道何时如何正确的使用J2EE API,就要使用更为实用的方式。

    计算机技术更新发展很快,新技术方面由于经验缺乏,通常我们要自己猜测如何正确使用这些技术,要通过不断的试验,直到找出最佳的方法,最佳的方法显然是从实践中得到的,不是发明出来的,而是发现和不断完善的。

    工程学产中的一大原则就是总结经验和利用实践证明行之有效的方案,软件开发也是这样。经验有助于更快更顺利的建立良好的解决方案,从而节省成本,提高质量。唯一的问题就是要需要获得经验,但这个经验可以从别人那里间接获得,而不一定需要自己的直接经验。别人的经验如何描述?多年来,模式已经成为收集,规范和分析某些情境中常见问题的有效方法,学习模式可以节省自己的时间,知道如何根据许多开发人员的间接经验进行合理的设计。

结束语

    在撰写这篇论文期间,我查阅了大量的技术书籍和文章,学到了很多的知识,使自己的技术水平得到了提高。同时得到了袁健美老师的悉心指导和帮助,在此,我向袁健美老师表达诚挚的谢意。

参考文献:

1.       Bruce Eckel. Think in Java第三版 .电子工业出版社. 北京. 2002

2.       Khawar Zaman Ahmed. J2EE和UML开发Java企业级应用程序. 清华大学出版社. 2003

3.       Crig Bery. 实用J2EE设计模式编程指南. www.china-pub.com. 2003                                

4.       孙卫琴. Tomcat与Java Web开发技术. 电子工业出版社. 2004

5.       孙卫琴. 精通Struts. 电子工业出版社. 2004

6.       Christopher Alexande. The Timeless Way of Building. Arrangement with Oxford University Press, Inc. 2003        

7.       夏昕. Hibernate开发指南. www.china-pub.com. 2004

没有评论: