新书推介:《语义网技术体系》
作者:瞿裕忠,胡伟,程龚
   XML论坛     W3CHINA.ORG讨论区     计算机科学论坛     SOAChina论坛     Blog     开放翻译计划     新浪微博  
 
  • 首页
  • 登录
  • 注册
  • 软件下载
  • 资料下载
  • 核心成员
  • 帮助
  •   Add to Google

    >> 本版讨论Java, J2SE, J2ME, J2EE, 以及Eclipse, NetBeans, JBuilder等Java开发环境,还有JSP, JavaServlet, JavaBean, EJB以及struts, hibernate, spring, webwork2, Java 3D, JOGL等相关技术。
    [返回] 中文XML论坛 - 专业的XML技术讨论区计算机技术与应用『 Java/Eclipse 』 → 设计模式之Interpreter(解释器) 查看新帖用户列表

      发表一个新主题  发表一个新投票  回复主题  (订阅本版) 您是本帖的第 17299 个阅读者浏览上一篇主题  刷新本主题   树形显示贴子 浏览下一篇主题
     * 贴子主题: 设计模式之Interpreter(解释器) 举报  打印  推荐  IE收藏夹 
       本主题类别:     
     npuhetao 帅哥哟,离线,有人找我吗?
      
      
      威望:5
      头衔:骨灰级菜鸟
      等级:大二(研究C++)
      文章:230
      积分:2708
      门派:XML.ORG.CN
      注册:2004/3/18

    姓名:(无权查看)
    城市:(无权查看)
    院校:(无权查看)
    给npuhetao发送一个短消息 把npuhetao加入好友 查看npuhetao的个人资料 搜索npuhetao在『 Java/Eclipse 』的所有贴子 引用回复这个贴子 回复这个贴子 查看npuhetao的博客11
    发贴心情 

    设计模式之Bridge
    Bridge模式定义 :
    将抽象和行为划分开来,各自独立,但能动态的结合。

    任何事物对象都有抽象和行为之分,例如人,人是一种抽象,人分男人和女人等;人有行为,行为也有各种具体表现,所以,“人”与“人的行为”两个概念也反映了抽象和行为之分。

    在面向对象设计的基本概念中,对象这个概念实际是由属性和行为两个部分组成的,属性我们可以认为是一种静止的,是一种抽象,一般情况下,行为是包含在一个对象中,但是,在有的情况下,我们需要将这些行为也进行归类,形成一个总的行为接口,这就是桥模式的用处。

    为什么使用?
    不希望抽象部分和行为有一种固定的绑定关系,而是应该可以动态联系的。

    如果一个抽象类或接口有多个具体实现(子类、concrete subclass),这些子类之间关系可能有以下两种情况:
    1. 这多个子类之间概念是并列的,如前面举例,打桩,有两个concrete class:方形桩和圆形桩;这两个形状上的桩是并列的,没有概念上的重复。

    2.这多个子类之中有内容概念上重叠.那么需要我们把抽象共同部分和行为共同部分各自独立开来,原来是准备放在一个接口里,现在需要设计两个接口:抽象接口和行为接口,分别放置抽象和行为.

    例如,一杯咖啡为例,子类实现类为四个:中杯加奶、大杯加奶、 中杯不加奶、大杯不加奶。

    但是,我们注意到:上面四个子类中有概念重叠,可从另外一个角度进行考虑,这四个类实际是两个角色的组合:抽象 和行为,其中抽象为:中杯和大杯;行为为:加奶 不加奶(如加橙汁 加苹果汁).

    实现四个子类在抽象和行为之间发生了固定的绑定关系,如果以后动态增加加葡萄汁的行为,就必须再增加两个类:中杯加葡萄汁和大杯加葡萄汁。显然混乱,扩展性极差。

    那我们从分离抽象和行为的角度,使用Bridge模式来实现。

    如何实现?
    以上面提到的咖啡 为例. 我们原来打算只设计一个接口(抽象类),使用Bridge模式后,我们需要将抽象和行为分开,加奶和不加奶属于行为,我们将它们抽象成一个专门的行为接口.

    先看看抽象部分的接口代码:


    public abstract class Coffee
    {
      CoffeeImp coffeeImp;

      public void setCoffeeImp() {
        this.CoffeeImp = CoffeeImpSingleton.getTheCoffeImp();
      }

      public CoffeeImp getCoffeeImp() {return this.CoffeeImp;}

      public abstract void pourCoffee();
    }

    其中CoffeeImp 是加不加奶的行为接口,看其代码如下:

    public abstract class CoffeeImp
    {
      public abstract void pourCoffeeImp();
    }

    现在我们有了两个抽象类,下面我们分别对其进行继承,实现concrete class:

    //中杯
    public class MediumCoffee extends Coffee
    {
      public MediumCoffee() {setCoffeeImp();}

      public void pourCoffee()
      {
        CoffeeImp coffeeImp = this.getCoffeeImp();
        //我们以重复次数来说明是冲中杯还是大杯 ,重复2次是中杯
        for (int i = 0; i < 2; i++)
        {

          coffeeImp.pourCoffeeImp();
        }
      
      }
    }


    //大杯
    public class SuperSizeCoffee extends Coffee
    {
      public SuperSizeCoffee() {setCoffeeImp();}

      public void pourCoffee()
      {
        CoffeeImp coffeeImp = this.getCoffeeImp();
        //我们以重复次数来说明是冲中杯还是大杯 ,重复5次是大杯
        for (int i = 0; i < 5; i++)
        {

          coffeeImp.pourCoffeeImp();
        }
      
      }
    }

    上面分别是中杯和大杯的具体实现.下面再对行为CoffeeImp进行继承:

    //加奶
    public class MilkCoffeeImp extends CoffeeImp
    {
      MilkCoffeeImp() {}

      public void pourCoffeeImp()
      {
        System.out.println("加了美味的牛奶");
      }
    }


    //不加奶
    public class FragrantCoffeeImp extends CoffeeImp
    {
      FragrantCoffeeImp() {}

      public void pourCoffeeImp()
      {
        System.out.println("什么也没加,清香");
      }
    }

    Bridge模式的基本框架我们已经搭好了,别忘记定义中还有一句:动态结合,我们现在可以喝到至少四种咖啡:
    1.中杯加奶
    2.中杯不加奶
    3.大杯加奶
    4.大杯不加奶

    看看是如何动态结合的,在使用之前,我们做个准备工作,设计一个单态类(Singleton)用来hold当前的CoffeeImp:

    public class CoffeeImpSingleton
    {
      private static CoffeeImp coffeeImp;

      public CoffeeImpSingleton(CoffeeImp coffeeImpIn)
       {this.coffeeImp = coffeeImpIn;}

      public static CoffeeImp getTheCoffeeImp()
      {
        return coffeeImp;
      }
    }

    看看中杯加奶 和大杯加奶 是怎么出来的:

    //拿出牛奶
    CoffeeImpSingleton coffeeImpSingleton = new CoffeeImpSingleton(new MilkCoffeeImp());

    //中杯加奶
    MediumCoffee mediumCoffee = new MediumCoffee();
    mediumCoffee.pourCoffee();

    //大杯加奶
    SuperSizeCoffee superSizeCoffee = new SuperSizeCoffee();
    superSizeCoffee.pourCoffee();

    注意: Bridge模式的执行类如CoffeeImp和Coffee是一对一的关系, 正确创建CoffeeImp是该模式的关键,

    Bridge模式在EJB中的应用
    EJB中有一个Data Access Object (DAO)模式,这是将商业逻辑和具体数据资源分开的,因为不同的数据库有不同的数据库操作.将操作不同数据库的行为独立抽象成一个行为接口DAO.如下:

    1.Business Object (类似Coffee)

    实现一些抽象的商业操作:如寻找一个用户下所有的订单

    涉及数据库操作都使用DAOImplementor.

    2.Data Access Object (类似CoffeeImp)

    一些抽象的对数据库资源操作

    3.DAOImplementor 如OrderDAOCS, OrderDAOOracle, OrderDAOSybase(类似MilkCoffeeImp FragrantCoffeeImp)

    具体的数据库操作,如"INSERT INTO "等语句,OrderDAOOracle是Oracle OrderDAOSybase是Sybase数据库.

    4.数据库 (Cloudscape, Oracle, or Sybase database via JDBC API)

    ----------------------------------------------
    你硬要把单纯的事情看得很严重,那样子你会很痛苦。

    点击查看用户来源及管理<br>发贴IP:*.*.*.* 2005/3/15 10:06:00
     
     npuhetao 帅哥哟,离线,有人找我吗?
      
      
      威望:5
      头衔:骨灰级菜鸟
      等级:大二(研究C++)
      文章:230
      积分:2708
      门派:XML.ORG.CN
      注册:2004/3/18

    姓名:(无权查看)
    城市:(无权查看)
    院校:(无权查看)
    给npuhetao发送一个短消息 把npuhetao加入好友 查看npuhetao的个人资料 搜索npuhetao在『 Java/Eclipse 』的所有贴子 引用回复这个贴子 回复这个贴子 查看npuhetao的博客12
    发贴心情 
    设计模式之Decorator(油漆工)

    装饰模式:Decorator常被翻译成"装饰",我觉得翻译成"油漆工"更形象点,油漆工(decorator)是用来刷油漆的,那么被刷油漆的对象我们称decoratee.这两种实体在Decorator模式中是必须的.


    Decorator定义:
    动态给一个对象添加一些额外的职责,就象在墙上刷油漆.使用Decorator模式相比用生成子类方式达到功能的扩充显得更为灵活.


    为什么使用Decorator?
    我们通常可以使用继承来实现功能的拓展,如果这些需要拓展的功能的种类很繁多,那么势必生成很多子类,增加系统的复杂性,同时,使用继承实现功能拓展,我们必须可预见这些拓展功能,这些功能是编译时就确定了,是静态的.

    使用Decorator的理由是:这些功能需要由用户动态决定加入的方式和时机.Decorator提供了"即插即用"的方法,在运行期间决定何时增加何种功能.

    如何使用?
    举Adapter中的打桩示例,在Adapter中有两种类:方形桩 圆形桩,Adapter模式展示如何综合使用这两个类,在Decorator模式中,我们是要在打桩时增加一些额外功能,比如,挖坑 在桩上钉木板等,不关心如何使用两个不相关的类.

    我们先建立一个接口:

    public interface Work
    {
      public void insert();

    }

    接口Work有一个具体实现:插入方形桩或圆形桩,这两个区别对Decorator是无所谓.我们以插入方形桩为例:

    public class SquarePeg implements Work{
      public void insert(){
        System.out.println("方形桩插入");
      }
    }

    现在有一个应用:需要在桩打入前,挖坑,在打入后,在桩上钉木板,这些额外的功能是动态,可能随意增加调整修改,比如,可能又需要在打桩之后钉架子(只是比喻).

    那么我们使用Decorator模式,这里方形桩SquarePeg是decoratee(被刷油漆者),我们需要在decoratee上刷些"油漆",这些油漆就是那些额外的功能.

    public class Decorator implements Work{

      private Work work;
      //额外增加的功能被打包在这个List中
      private ArrayList others = new ArrayList();

      //在构造器中使用组合new方式,引入Work对象;
      public Decorator(Work work)
      {
        this.work=work;
      
        others.add("挖坑");

        others.add("钉木板");
      }

      public void insert(){

        newMethod();
      }

      
      //在新方法中,我们在insert之前增加其他方法,这里次序先后是用户灵活指定的   
      public void newMethod()
      {
        otherMethod();
        work.insert();


      }


      public void otherMethod()
      {
        ListIterator listIterator = others.listIterator();
        while (listIterator.hasNext())
        {
          System.out.println(((String)(listIterator.next())) + " 正在进行");
        }

      }


    }

    在上例中,我们把挖坑和钉木板都排在了打桩insert前面,这里只是举例说明额外功能次序可以任意安排.

    好了,Decorator模式出来了,我们看如何调用:

    Work squarePeg = new SquarePeg();
    Work decorator = new Decorator(squarePeg);
    decorator.insert();


    Decorator模式至此完成.

    如果你细心,会发现,上面调用类似我们读取文件时的调用:

    FileReader fr = new FileReader(filename);
    BufferedReader br = new BufferedReader(fr);

    实际上Java 的I/O API就是使用Decorator实现的,I/O变种很多,如果都采取继承方法,将会产生很多子类,显然相当繁琐.

    Jive中的Decorator实现
    在论坛系统中,有些特别的字是不能出现在论坛中如"打倒XXX",我们需要过滤这些"反动"的字体.不让他们出现或者高亮度显示.

    在IBM Java专栏中专门谈Jive的文章中,有谈及Jive中ForumMessageFilter.java使用了Decorator模式,其实,该程序并没有真正使用Decorator,而是提示说:针对特别论坛可以设计额外增加的过滤功能,那么就可以重组ForumMessageFilter作为Decorator模式了.

    所以,我们在分辨是否真正是Decorator模式,以及会真正使用Decorator模式,一定要把握好Decorator模式的定义,以及其中参与的角色(Decoratee 和Decorator).

    ----------------------------------------------
    你硬要把单纯的事情看得很严重,那样子你会很痛苦。

    点击查看用户来源及管理<br>发贴IP:*.*.*.* 2005/3/15 10:06:00
     
     npuhetao 帅哥哟,离线,有人找我吗?
      
      
      威望:5
      头衔:骨灰级菜鸟
      等级:大二(研究C++)
      文章:230
      积分:2708
      门派:XML.ORG.CN
      注册:2004/3/18

    姓名:(无权查看)
    城市:(无权查看)
    院校:(无权查看)
    给npuhetao发送一个短消息 把npuhetao加入好友 查看npuhetao的个人资料 搜索npuhetao在『 Java/Eclipse 』的所有贴子 引用回复这个贴子 回复这个贴子 查看npuhetao的博客13
    发贴心情 
    设计模式之Proxy(代理)

    理解并使用设计模式,能够培养我们良好的面向对象编程习惯,同时在实际应用中,可以如鱼得水,享受游刃有余的乐趣.

    代理模式是比较有用途的一种模式,而且变种较多,应用场合覆盖从小结构到整个系统的大结构,Proxy是代理的意思,我们也许有代理服务器等概念,代理概念可以解释为:在出发点到目的地之间有一道中间层,意为代理.

    设计模式中定义: 为其他对象提供一种代理以控制对这个对象的访问.

    为什么要使用Proxy?
    1.授权机制 不同级别的用户对同一对象拥有不同的访问权利,如Jive论坛系统中,就使用Proxy进行授权机制控制,访问论坛有两种人:注册用户和游客(未注册用户),Jive中就通过类似ForumProxy这样的代理来控制这两种用户对论坛的访问权限.

    2.某个客户端不能直接操作到某个对象,但又必须和那个对象有所互动.
    举例两个具体情况:
    (1)如果那个对象是一个是很大的图片,需要花费很长时间才能显示出来,那么当这个图片包含在文档中时,使用编辑器或浏览器打开这个文档,打开文档必须很迅速,不能等待大图片处理完成,这时需要做个图片Proxy来代替真正的图片.

    (2)如果那个对象在Internet的某个远端服务器上,直接操作这个对象因为网络速度原因可能比较慢,那我们可以先用Proxy来代替那个对象.


    总之原则是,对于开销很大的对象,只有在使用它时才创建,这个原则可以为我们节省很多宝贵的Java内存. 所以,有些人认为Java耗费资源内存,我以为这和程序编制思路也有一定的关系.

    如何使用Proxy?
    以Jive论坛系统为例,访问论坛系统的用户有多种类型:注册普通用户 论坛管理者 系统管理者 游客,注册普通用户才能发言;论坛管理者可以管理他被授权的论坛;系统管理者可以管理所有事务等,这些权限划分和管理是使用Proxy完成的.

    Forum是Jive的核心接口,在Forum中陈列了有关论坛操作的主要行为,如论坛名称 论坛描述的获取和修改,帖子发表删除编辑等.

    在ForumPermissions中定义了各种级别权限的用户:

    public class ForumPermissions implements Cacheable {
    /**
    * Permission to read object.
    */
    public static final int READ = 0;

    /**
    * Permission to administer the entire sytem.
    */
    public static final int SYSTEM_ADMIN = 1;

    /**
    * Permission to administer a particular forum.
    */
    public static final int FORUM_ADMIN = 2;

    /**
    * Permission to administer a particular user.
    */
    public static final int USER_ADMIN = 3;

    /**
    * Permission to administer a particular group.
    */
    public static final int GROUP_ADMIN = 4;

    /**
    * Permission to moderate threads.
    */
    public static final int MODERATE_THREADS = 5;

    /**
    * Permission to create a new thread.
    */
    public static final int CREATE_THREAD = 6;

    /**
    * Permission to create a new message.
    */
    public static final int CREATE_MESSAGE = 7;

    /**
    * Permission to moderate messages.
    */
    public static final int MODERATE_MESSAGES = 8;

    .....

    public boolean isSystemOrForumAdmin() {
      return (values[FORUM_ADMIN] || values[SYSTEM_ADMIN]);
    }

    .....

    }

    因此,Forum中各种操作权限是和ForumPermissions定义的用户级别有关系的,作为接口Forum的实现:ForumProxy正是将这种对应关系联系起来.比如,修改Forum的名称,只有论坛管理者或系统管理者可以修改,代码如下:

    public class ForumProxy implements Forum {

    private ForumPermissions permissions;
    private Forum forum;
    this.authorization = authorization;

    public ForumProxy(Forum forum, Authorization authorization,
    ForumPermissions permissions)
    {
    this.forum = forum;
    this.authorization = authorization;
    this.permissions = permissions;
    }

    .....

    public void setName(String name) throws UnauthorizedException,
    ForumAlreadyExistsException
    {
      //只有是系统或论坛管理者才可以修改名称
      if (permissions.isSystemOrForumAdmin()) {
        forum.setName(name);
      }
      else {
        throw new UnauthorizedException();
      }
    }

    ...

    }

    而DbForum才是接口Forum的真正实现,以修改论坛名称为例:

    public class DbForum implements Forum, Cacheable {
    ...

    public void setName(String name) throws ForumAlreadyExistsException {

      ....

      this.name = name;
      //这里真正将新名称保存到数据库中
      saveToDb();

      ....
    }

    ...

    }

    凡是涉及到对论坛名称修改这一事件,其他程序都首先得和ForumProxy打交道,由ForumProxy决定是否有权限做某一样事情,ForumProxy是个名副其实的"网关","安全代理系统".

    在平时应用中,无可避免总要涉及到系统的授权或安全体系,不管你有无意识的使用Proxy,实际你已经在使用Proxy了.

    我们继续结合Jive谈入深一点,下面要涉及到工厂模式了,如果你不了解工厂模式,请看我的另外一篇文章:设计模式之Factory

    我们已经知道,使用Forum需要通过ForumProxy,Jive中创建一个Forum是使用Factory模式,有一个总的抽象类ForumFactory,在这个抽象类中,调用ForumFactory是通过getInstance()方法实现,这里使用了Singleton(也是设计模式之一,由于介绍文章很多,我就不写了,看这里),getInstance()返回的是ForumFactoryProxy.

    为什么不返回ForumFactory,而返回ForumFactory的实现ForumFactoryProxy?
    原因是明显的,需要通过代理确定是否有权限创建forum.

    在ForumFactoryProxy中我们看到代码如下:

    public class ForumFactoryProxy extends ForumFactory {
      protected ForumFactory factory;
      protected Authorization authorization;
      protected ForumPermissions permissions;

      public ForumFactoryProxy(Authorization authorization, ForumFactory factory,
      ForumPermissions permissions)
      {
        this.factory = factory;
        this.authorization = authorization;
        this.permissions = permissions;
      }

      public Forum createForum(String name, String description)
          throws UnauthorizedException, ForumAlreadyExistsException
      {
        //只有系统管理者才可以创建forum
        if (permissions.get(ForumPermissions.SYSTEM_ADMIN)) {
          Forum newForum = factory.createForum(name, description);
          return new ForumProxy(newForum, authorization, permissions);
        }
        else {
          throw new UnauthorizedException();
      }
    }

    方法createForum返回的也是ForumProxy, Proxy就象一道墙,其他程序只能和Proxy交互操作.

    注意到这里有两个Proxy:ForumProxy和ForumFactoryProxy. 代表两个不同的职责:使用Forum和创建Forum;
    至于为什么将使用对象和创建对象分开,这也是为什么使用Factory模式的原因所在:是为了"封装" "分派";换句话说,尽可能功能单一化,方便维护修改.

    Jive论坛系统中其他如帖子的创建和使用,都是按照Forum这个思路而来的.

    以上我们讨论了如何使用Proxy进行授权机制的访问,Proxy还可以对用户隐藏另外一种称为copy-on-write的优化方式.拷贝一个庞大而复杂的对象是一个开销很大的操作,如果拷贝过程中,没有对原来的对象有所修改,那么这样的拷贝开销就没有必要.用代理延迟这一拷贝过程.

    比如:我们有一个很大的Collection,具体如hashtable,有很多客户端会并发同时访问它.其中一个特别的客户端要进行连续的数据获取,此时要求其他客户端不能再向hashtable中增加或删除 东东.

    最直接的解决方案是:使用collection的lock,让这特别的客户端获得这个lock,进行连续的数据获取,然后再释放lock.
    public void foFetches(Hashtable ht){
      synchronized(ht){
        //具体的连续数据获取动作..
      }

    }


    但是这一办法可能锁住Collection会很长时间,这段时间,其他客户端就不能访问该Collection了.

    第二个解决方案是clone这个Collection,然后让连续的数据获取针对clone出来的那个Collection操作.这个方案前提是,这个Collection是可clone的,而且必须有提供深度clone的方法.Hashtable就提供了对自己的clone方法,但不是Key和value对象的clone,关于Clone含义可以参考专门文章.
    public void foFetches(Hashtable ht){


      Hashttable newht=(Hashtable)ht.clone();

    }

    问题又来了,由于是针对clone出来的对象操作,如果原来的母体被其他客户端操作修改了, 那么对clone出来的对象操作就没有意义了.

    最后解决方案:我们可以等其他客户端修改完成后再进行clone,也就是说,这个特别的客户端先通过调用一个叫clone的方法来进行一系列数据获取操作.但实际上没有真正的进行对象拷贝,直至有其他客户端修改了这个对象Collection.

    使用Proxy实现这个方案.这就是copy-on-write操作.

    Proxy应用范围很广,现在流行的分布计算方式RMI和Corba等都是Proxy模式的应用.

    ----------------------------------------------
    你硬要把单纯的事情看得很严重,那样子你会很痛苦。

    点击查看用户来源及管理<br>发贴IP:*.*.*.* 2005/3/15 10:07:00
     
     npuhetao 帅哥哟,离线,有人找我吗?
      
      
      威望:5
      头衔:骨灰级菜鸟
      等级:大二(研究C++)
      文章:230
      积分:2708
      门派:XML.ORG.CN
      注册:2004/3/18

    姓名:(无权查看)
    城市:(无权查看)
    院校:(无权查看)
    给npuhetao发送一个短消息 把npuhetao加入好友 查看npuhetao的个人资料 搜索npuhetao在『 Java/Eclipse 』的所有贴子 引用回复这个贴子 回复这个贴子 查看npuhetao的博客14
    发贴心情 
    设计模式之Facade(外观 总管 Manager)
    Facade模式的定义: 为子系统中的一组接口提供一个一致的界面.

    Facade一个典型应用就是数据库JDBC的应用,如下例对数据库的操作:

    public class DBCompare {


      Connection conn = null;
      PreparedStatement prep = null;
      ResultSet rset = null;
      try {
         Class.forName( "<driver>" ).newInstance();
         conn = DriverManager.getConnection( "<database>" );
        
         String sql = "SELECT * FROM <table> WHERE <column name> = ?";
         prep = conn.prepareStatement( sql );
         prep.setString( 1, "<column value>" );
         rset = prep.executeQuery();
         if( rset.next() ) {
            System.out.println( rset.getString( "<column name" ) );
         }
      } catch( SException e ) {
         e.printStackTrace();
      } finally {
         rset.close();
         prep.close();
         conn.close();
      }
    }

    上例是Jsp中最通常的对数据库操作办法.

    在应用中,经常需要对数据库操作,每次都写上述一段代码肯定比较麻烦,需要将其中不变的部分提炼出来,做成一个接口,这就引入了facade外观对象.如果以后我们更换Class.forName中的<driver>也非常方便,比如从Mysql数据库换到Oracle数据库,只要更换facade接口中的driver就可以.

    我们做成了一个Facade接口,使用该接口,上例中的程序就可以更改如下:

    public class DBCompare {

      String sql = "SELECT * FROM <table> WHERE <column name> = ?";  

      try {
         Mysql msql=new mysql(sql);
         prep.setString( 1, "<column value>" );
         rset = prep.executeQuery();
         if( rset.next() ) {
            System.out.println( rset.getString( "<column name" ) );
         }
      } catch( SException e ) {
         e.printStackTrace();
      } finally {
         mysql.close();
         mysql=null;
      }
    }

    可见非常简单,所有程序对数据库访问都是使用改接口,降低系统的复杂性,增加了灵活性.

    如果我们要使用连接池,也只要针对facade接口修改就可以.

    由上图可以看出, facade实际上是个理顺系统间关系,降低系统间耦合度的一个常用的办法,也许你已经不知不觉在使用,尽管不知道它就是facade.

    ----------------------------------------------
    你硬要把单纯的事情看得很严重,那样子你会很痛苦。

    点击查看用户来源及管理<br>发贴IP:*.*.*.* 2005/3/15 10:08:00
     
     npuhetao 帅哥哟,离线,有人找我吗?
      
      
      威望:5
      头衔:骨灰级菜鸟
      等级:大二(研究C++)
      文章:230
      积分:2708
      门派:XML.ORG.CN
      注册:2004/3/18

    姓名:(无权查看)
    城市:(无权查看)
    院校:(无权查看)
    给npuhetao发送一个短消息 把npuhetao加入好友 查看npuhetao的个人资料 搜索npuhetao在『 Java/Eclipse 』的所有贴子 引用回复这个贴子 回复这个贴子 查看npuhetao的博客15
    发贴心情 
    设计模式之Singleton(单态)

    单态定义:
    Singleton模式主要作用是保证在Java应用程序中,一个类Class只有一个实例存在。

    在很多操作中,比如建立目录 数据库连接都需要这样的单线程操作。

    还有, singleton能够被状态化; 这样,多个单态类在一起就可以作为一个状态仓库一样向外提供服务,比如,你要论坛中的帖子计数器,每次浏览一次需要计数,单态类能否保持住这个计数,并且能synchronize的安全自动加1,如果你要把这个数字永久保存到数据库,你可以在不修改单态接口的情况下方便的做到。

    另外方面,Singleton也能够被无状态化。提供工具性质的功能,

    Singleton模式就为我们提供了这样实现的可能。使用Singleton的好处还在于可以节省内存,因为它限制了实例的个数,有利于Java垃圾回收(garbage collection)。

    我们常常看到工厂模式中类装入器(class loader)中也用Singleton模式实现的,因为被装入的类实际也属于资源。


    如何使用?
    一般Singleton模式通常有几种形式:

    public class Singleton {

      private Singleton(){}

      //在自己内部定义自己一个实例,是不是很奇怪?
      //注意这是private 只供内部调用

      private static Singleton instance = new Singleton();

      //这里提供了一个供外部访问本class的静态方法,可以直接访问  
      public static Singleton getInstance() {
        return instance;   
       }
    }


    第二种形式:

    public class Singleton {
      private static Singleton instance = null;

      public static synchronized Singleton getInstance() {

      //这个方法比上面有所改进,不用每次都进行生成对象,只是第一次     
      //使用时生成实例,提高了效率!
      if (instance==null)
        instance=new Singleton();
      return instance;   }

    }


    使用Singleton.getInstance()可以访问单态类。

    上面第二中形式是lazy initialization,也就是说第一次调用时初始Singleton,以后就不用再生成了。

    注意到lazy initialization形式中的synchronized,这个synchronized很重要,如果没有synchronized,那么使用getInstance()是有可能得到多个Singleton实例。关于lazy initialization的Singleton有很多涉及double-checked locking (DCL)的讨论,有兴趣者进一步研究。

    一般认为第一种形式要更加安全些。


    使用Singleton注意事项:
    有时在某些情况下,使用Singleton并不能达到Singleton的目的,如有多个Singleton对象同时被不同的类装入器装载;在EJB这样的分布式系统中使用也要注意这种情况,因为EJB是跨服务器,跨JVM的。

    我们以SUN公司的宠物店源码(Pet Store 1.3.1)的ServiceLocator为例稍微分析一下:

    在Pet Store中ServiceLocator有两种,一个是EJB目录下;一个是WEB目录下,我们检查这两个ServiceLocator会发现内容差不多,都是提供EJB的查询定位服务,可是为什么要分开呢?仔细研究对这两种ServiceLocator才发现区别:在WEB中的ServiceLocator的采取Singleton模式,ServiceLocator属于资源定位,理所当然应该使用Singleton模式。但是在EJB中,Singleton模式已经失去作用,所以ServiceLocator才分成两种,一种面向WEB服务的,一种是面向EJB服务的。

    Singleton模式看起来简单,使用方法也很方便,但是真正用好,是非常不容易,需要对Java的类 线程 内存等概念有相当的了解。

    总之:如果你的应用基于容器,那么Singleton模式少用或者不用,可以使用相关替代技术。

    ----------------------------------------------
    你硬要把单纯的事情看得很严重,那样子你会很痛苦。

    点击查看用户来源及管理<br>发贴IP:*.*.*.* 2005/3/15 10:08:00
     
     npuhetao 帅哥哟,离线,有人找我吗?
      
      
      威望:5
      头衔:骨灰级菜鸟
      等级:大二(研究C++)
      文章:230
      积分:2708
      门派:XML.ORG.CN
      注册:2004/3/18

    姓名:(无权查看)
    城市:(无权查看)
    院校:(无权查看)
    给npuhetao发送一个短消息 把npuhetao加入好友 查看npuhetao的个人资料 搜索npuhetao在『 Java/Eclipse 』的所有贴子 引用回复这个贴子 回复这个贴子 查看npuhetao的博客16
    发贴心情 
    设计模式之Builder

    Builder模式定义:
    将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示.

    Builder模式是一步一步创建一个复杂的对象,它允许用户可以只通过指定复杂对象的类型和内容就可以构建它们.用户不知道内部的具体构建细节.Builder模式是非常类似抽象工厂模式,细微的区别大概只有在反复使用中才能体会到.

    为何使用?
    是为了将构建复杂对象的过程和它的部件解耦.注意: 是解耦过程和部件.

    因为一个复杂的对象,不但有很多大量组成部分,如汽车,有很多部件:车轮 方向盘 发动机还有各种小零件等等,部件很多,但远不止这些,如何将这些部件装配成一辆汽车,这个装配过程也很复杂(需要很好的组装技术),Builder模式就是为了将部件和组装过程分开.


    如何使用?
    首先假设一个复杂对象是由多个部件组成的,Builder模式是把复杂对象的创建和部件的创建分别开来,分别用Builder类和Director类来表示.


    首先,需要一个接口,它定义如何创建复杂对象的各个部件:

    public interface Builder {

      //创建部件A  比如创建汽车车轮
      void buildPartA();
      //创建部件B 比如创建汽车方向盘
      void buildPartB();
      //创建部件C 比如创建汽车发动机
      void buildPartC();

      //返回最后组装成品结果 (返回最后装配好的汽车)
      //成品的组装过程不在这里进行,而是转移到下面的Director类中进行.
      //从而实现了解耦过程和部件
      Product getResult();

    }

    用Director构建最后的复杂对象,而在上面Builder接口中封装的是如何创建一个个部件(复杂对象是由这些部件组成的),也就是说Director的内容是如何将部件最后组装成成品:

    public class Director {

      private Builder builder;

      public Director( Builder builder ) {
        this.builder = builder;
      }
      // 将部件partA partB partC最后组成复杂对象
      //这里是将车轮 方向盘和发动机组装成汽车的过程
      public void construct() {
        builder.buildPartA();
        builder.buildPartB();
        builder.buildPartC();

      }

    }

    Builder的具体实现ConcreteBuilder:
    通过具体完成接口Builder来构建或装配产品的部件;
    定义并明确它所要创建的是什么具体东西;
    提供一个可以重新获取产品的接口:

    public class ConcreteBuilder implements Builder {

      Part partA, partB, partC;
      public void buildPartA() {
        //这里是具体如何构建partA的代码

      };
      public void buildPartB() {
        //这里是具体如何构建partB的代码
      };
       public void buildPartC() {
        //这里是具体如何构建partB的代码
      };
       public Product getResult() {
        //返回最后组装成品结果
      };


    }

    复杂对象:产品Product:


    public interface Product { }  

    复杂对象的部件:

    public interface Part { }


    我们看看如何调用Builder模式:
    ConcreteBuilder builder = new ConcreteBuilder();
    Director director = new Director( builder );

    director.construct();
    Product product = builder.getResult();

    Builder模式的应用
    在Java实际使用中,我们经常用到"池"(Pool)的概念,当资源提供者无法提供足够的资源,并且这些资源需要被很多用户反复共享时,就需要使用池.

    "池"实际是一段内存,当池中有一些复杂的资源的"断肢"(比如数据库的连接池,也许有时一个连接会中断),如果循环再利用这些"断肢",将提高内存使用效率,提高池的性能.修改Builder模式中Director类使之能诊断"断肢"断在哪个部件上,再修复这个部件.

    ----------------------------------------------
    你硬要把单纯的事情看得很严重,那样子你会很痛苦。

    点击查看用户来源及管理<br>发贴IP:*.*.*.* 2005/3/15 10:08:00
     
     npuhetao 帅哥哟,离线,有人找我吗?
      
      
      威望:5
      头衔:骨灰级菜鸟
      等级:大二(研究C++)
      文章:230
      积分:2708
      门派:XML.ORG.CN
      注册:2004/3/18

    姓名:(无权查看)
    城市:(无权查看)
    院校:(无权查看)
    给npuhetao发送一个短消息 把npuhetao加入好友 查看npuhetao的个人资料 搜索npuhetao在『 Java/Eclipse 』的所有贴子 引用回复这个贴子 回复这个贴子 查看npuhetao的博客17
    发贴心情 
    设计模式之Prototype(原型)

    原型模式定义:
    用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象.

    Prototype模式允许一个对象再创建另外一个可定制的对象,根本无需知道任何如何创建的细节,工作原理是:通过将一个原型对象传给那个要发动创建的对象,这个要发动创建的对象通过请求原型对象拷贝它们自己来实施创建。

    如何使用?
    因为Java中的提供clone()方法来实现对象的克隆,所以Prototype模式实现一下子变得很简单.

    以勺子为例:

    public abstract class AbstractSpoon implements Cloneable
    {
      String spoonName;

      public void setSpoonName(String spoonName) {this.spoonName = spoonName;}
      public String getSpoonName() {return this.spoonName;}

      public Object clone()
      {
        Object object = null;
        try {
          object = super.clone();
        } catch (CloneNotSupportedException exception) {
          System.err.println("AbstractSpoon is not Cloneable");
        }
        return object;
      }
    }

    有个具体实现(ConcretePrototype):

    public class SoupSpoon extends AbstractSpoon
    {
      public SoupSpoon()
      {
        setSpoonName("Soup Spoon");
      }
    }


    调用Prototype模式很简单:

    AbstractSpoon spoon = new SoupSpoon();
    AbstractSpoon spoon2 = spoon.clone();

    当然也可以结合工厂模式来创建AbstractSpoon实例。

    在Java中Prototype模式变成clone()方法的使用,由于Java的纯洁的面向对象特性,使得在Java中使用设计模式变得很自然,两者已经几乎是浑然一体了。这反映在很多模式上,如Interator遍历模式。

    ----------------------------------------------
    你硬要把单纯的事情看得很严重,那样子你会很痛苦。

    点击查看用户来源及管理<br>发贴IP:*.*.*.* 2005/3/15 10:09:00
     
     npuhetao 帅哥哟,离线,有人找我吗?
      
      
      威望:5
      头衔:骨灰级菜鸟
      等级:大二(研究C++)
      文章:230
      积分:2708
      门派:XML.ORG.CN
      注册:2004/3/18

    姓名:(无权查看)
    城市:(无权查看)
    院校:(无权查看)
    给npuhetao发送一个短消息 把npuhetao加入好友 查看npuhetao的个人资料 搜索npuhetao在『 Java/Eclipse 』的所有贴子 引用回复这个贴子 回复这个贴子 查看npuhetao的博客18
    发贴心情 
    设计模式之Factory

    工厂模式定义:提供创建对象的接口.

    为何使用?
    工厂模式是我们最常用的模式了,著名的Jive论坛 ,就大量使用了工厂模式,工厂模式在Java程序系统可以说是随处可见。

    为什么工厂模式是如此常用?因为工厂模式就相当于创建实例对象的new,我们经常要根据类Class生成实例对象,如A a=new A() 工厂模式也是用来创建实例对象的,所以以后new时就要多个心眼,是否可以考虑实用工厂模式,虽然这样做,可能多做一些工作,但会给你系统带来更大的可扩展性和尽量少的修改量。

    我们以类Sample为例, 如果我们要创建Sample的实例对象:

    Sample sample=new Sample();

    可是,实际情况是,通常我们都要在创建sample实例时做点初始化的工作,比如赋值 查询数据库等。

    首先,我们想到的是,可以使用Sample的构造函数,这样生成实例就写成:

    Sample sample=new Sample(参数);

    但是,如果创建sample实例时所做的初始化工作不是象赋值这样简单的事,可能是很长一段代码,如果也写入构造函数中,那你的代码很难看了(就需要Refactor重整)。

    为什么说代码很难看,初学者可能没有这种感觉,我们分析如下,初始化工作如果是很长一段代码,说明要做的工作很多,将很多工作装入一个方法中,相当于将很多鸡蛋放在一个篮子里,是很危险的,这也是有背于Java面向对象的原则,面向对象的封装(Encapsulation)和分派(Delegation)告诉我们,尽量将长的代码分派“切割”成每段,将每段再“封装”起来(减少段和段之间偶合联系性),这样,就会将风险分散,以后如果需要修改,只要更改每段,不会再发生牵一动百的事情。

    在本例中,首先,我们需要将创建实例的工作与使用实例的工作分开, 也就是说,让创建实例所需要的大量初始化工作从Sample的构造函数中分离出去。

    这时我们就需要Factory工厂模式来生成对象了,不能再用上面简单new Sample(参数)。还有,如果Sample有个继承如MySample, 按照面向接口编程,我们需要将Sample抽象成一个接口.现在Sample是接口,有两个子类MySample 和HisSample .我们要实例化他们时,如下:

    Sample mysample=new MySample();
    Sample hissample=new HisSample();

    随着项目的深入,Sample可能还会"生出很多儿子出来", 那么我们要对这些儿子一个个实例化,更糟糕的是,可能还要对以前的代码进行修改:加入后来生出儿子的实例.这在传统程序中是无法避免的.

    但如果你一开始就有意识使用了工厂模式,这些麻烦就没有了.

    工厂方法
    你会建立一个专门生产Sample实例的工厂:

    public class Factory{

      public static Sample creator(int which){

      //getClass 产生Sample 一般可使用动态类装载装入类。
      if (which==1)
        return new SampleA();
      else if (which==2)
        return new SampleB();

      }

    }

    那么在你的程序中,如果要实例化Sample时.就使用

    Sample sampleA=Factory.creator(1);

    这样,在整个就不涉及到Sample的具体子类,达到封装效果,也就减少错误修改的机会,这个原理可以用很通俗的话来比喻:就是具体事情做得越多,越容易范错误.这每个做过具体工作的人都深有体会,相反,官做得越高,说出的话越抽象越笼统,范错误可能性就越少.好象我们从编程序中也能悟出人生道理?呵呵.

    使用工厂方法 要注意几个角色,首先你要定义产品接口,如上面的Sample,产品接口下有Sample接口的实现类,如SampleA,其次要有一个factory类,用来生成产品Sample,如下图,最右边是生产的对象Sample:

    进一步稍微复杂一点,就是在工厂类上进行拓展,工厂类也有继承它的实现类concreteFactory了。

    抽象工厂
    工厂模式中有: 工厂方法(Factory Method) 抽象工厂(Abstract Factory).

    这两个模式区别在于需要创建对象的复杂程度上。如果我们创建对象的方法变得复杂了,如上面工厂方法中是创建一个对象Sample,如果我们还有新的产品接口Sample2.

    这里假设:Sample有两个concrete类SampleA和SamleB,而Sample2也有两个concrete类Sample2A和SampleB2

    那么,我们就将上例中Factory变成抽象类,将共同部分封装在抽象类中,不同部分使用子类实现,下面就是将上例中的Factory拓展成抽象工厂:

    public abstract class Factory{

      public abstract Sample creator();

      public abstract Sample2 creator(String name);

    }

    public class SimpleFactory extends Factory{

      public Sample creator(){
        .........
        return new SampleA
      }

      public Sample2 creator(String name){
        .........
        return new Sample2A
      }

    }

    public class BombFactory extends Factory{

      public Sample creator(){
        ......
        return new SampleB
      }

      public Sample2 creator(String name){
        ......
        return new Sample2B
      }

    }


    从上面看到两个工厂各自生产出一套Sample和Sample2,也许你会疑问,为什么我不可以使用两个工厂方法来分别生产Sample和Sample2?

    抽象工厂还有另外一个关键要点,是因为 SimpleFactory内,生产Sample和生产Sample2的方法之间有一定联系,所以才要将这两个方法捆绑在一个类中,这个工厂类有其本身特征,也许制造过程是统一的,比如:制造工艺比较简单,所以名称叫SimpleFactory。


    在实际应用中,工厂方法用得比较多一些,而且是和动态类装入器组合在一起应用,

    举例

    我们以Jive的ForumFactory为例,这个例子在前面的Singleton模式中我们讨论过,现在再讨论其工厂模式:

    public abstract class ForumFactory {

      private static Object initLock = new Object();
      private static String className = "com.jivesoftware.forum.database.DbForumFactory";
      private static ForumFactory factory = null;

      public static ForumFactory getInstance(Authorization authorization) {
        //If no valid authorization passed in, return null.
        if (authorization == null) {
          return null;
        }
        //以下使用了Singleton 单态模式
        if (factory == null) {
          synchronized(initLock) {
            if (factory == null) {
                ......

              try {
                  //动态转载类
                  Class c = Class.forName(className);
                  factory = (ForumFactory)c.newInstance();
              }
              catch (Exception e) {
                  return null;
              }
            }
          }
        }

        //Now, 返回 proxy.用来限制授权对forum的访问
        return new ForumFactoryProxy(authorization, factory,
                        factory.getPermissions(authorization));
      }

      //真正创建forum的方法由继承forumfactory的子类去完成.
      public abstract Forum createForum(String name, String description)
      throws UnauthorizedException, ForumAlreadyExistsException;

      ....

    }


    因为现在的Jive是通过数据库系统存放论坛帖子等内容数据,如果希望更改为通过文件系统实现,这个工厂方法ForumFactory就提供了提供动态接口:

    private static String className = "com.jivesoftware.forum.database.DbForumFactory";

    你可以使用自己开发的创建forum的方法代替com.jivesoftware.forum.database.DbForumFactory就可以.

    在上面的一段代码中一共用了三种模式,除了工厂模式外,还有Singleton单态模式,以及proxy模式,proxy模式主要用来授权用户对forum的访问,因为访问forum有两种人:一个是注册用户 一个是游客guest,那么那么相应的权限就不一样,而且这个权限是贯穿整个系统的,因此建立一个proxy,类似网关的概念,可以很好的达到这个效果.  

    看看Java宠物店中的CatalogDAOFactory:

    public class CatalogDAOFactory {

      /**

      * 本方法制定一个特别的子类来实现DAO模式。
      * 具体子类定义是在J2EE的部署描述器中。
      */

      public static CatalogDAO getDAO() throws CatalogDAOSysException {

        CatalogDAO catDao = null;

        try {

          InitialContext ic = new InitialContext();
          //动态装入CATALOG_DAO_CLASS
          //可以定义自己的CATALOG_DAO_CLASS,从而在无需变更太多代码
          //的前提下,完成系统的巨大变更。

          String className =(String) ic.lookup(JNDINames.CATALOG_DAO_CLASS);

          catDao = (CatalogDAO) Class.forName(className).newInstance();

        } catch (NamingException ne) {

          throw new CatalogDAOSysException("
            CatalogDAOFactory.getDAO: NamingException while
              getting DAO type : " + ne.getMessage());

        } catch (Exception se) {

          throw new CatalogDAOSysException("
            CatalogDAOFactory.getDAO: Exception while getting
              DAO type : " + se.getMessage());

        }

        return catDao;

      }

    }


    CatalogDAOFactory是典型的工厂方法,catDao是通过动态类装入器className获得CatalogDAOFactory具体实现子类,这个实现子类在Java宠物店是用来操作catalog数据库,用户可以根据数据库的类型不同,定制自己的具体实现子类,将自己的子类名给与CATALOG_DAO_CLASS变量就可以。

    由此可见,工厂方法确实为系统结构提供了非常灵活强大的动态扩展机制,只要我们更换一下具体的工厂方法,系统其他地方无需一点变换,就有可能将系统功能进行改头换面的变化。

    ----------------------------------------------
    你硬要把单纯的事情看得很严重,那样子你会很痛苦。

    点击查看用户来源及管理<br>发贴IP:*.*.*.* 2005/3/15 10:09:00
     
     GoogleAdSense
      
      
      等级:大一新生
      文章:1
      积分:50
      门派:无门无派
      院校:未填写
      注册:2007-01-01
    给Google AdSense发送一个短消息 把Google AdSense加入好友 查看Google AdSense的个人资料 搜索Google AdSense在『 Java/Eclipse 』的所有贴子 访问Google AdSense的主页 引用回复这个贴子 回复这个贴子 查看Google AdSense的博客广告
    2026/1/29 0:36:19

    本主题贴数18,分页: [1] [2]

    管理选项修改tag | 锁定 | 解锁 | 提升 | 删除 | 移动 | 固顶 | 总固顶 | 奖励 | 惩罚 | 发布公告
    W3C Contributing Supporter! W 3 C h i n a ( since 2003 ) 旗 下 站 点
    苏ICP备05006046号《全国人大常委会关于维护互联网安全的决定》《计算机信息网络国际联网安全保护管理办法》
    187.012ms