Factory Pattern

来源:互联网 发布:京东杭州程序员招聘 编辑:程序博客网 时间:2024/06/06 14:03

      工厂模式主要是为创建对象提供过渡接口,以便将创建对象的具体过程屏蔽隔离起来,达到提高灵活性的目的。

工厂模式在《Java与模式》中分为三类:
    1
)简单工厂模式(Simple Factory

2)工厂方法模式(Factory Method

3)抽象工厂模式(Abstract Factory
       这三种模式从上到下逐步抽象,并且更具一般性。
       GOF
在《设计模式》一书中将工厂模式分为两类:工厂方法模式(FactoryMethod)与抽象工厂模式(AbstractFactory)。将简单工厂模式(SimpleFactory)看为工厂方法模式的一种特例,两者归为一类。

 

简单工厂模式:

 

简单工厂模式是类的创建模式,又叫静态工厂方法(Static Factory Method)模式。使用静态工厂方法是为了将具体子类实例化的工作隐藏起来,从而客户端不必考虑如何将具体子类实例化,因为抽象类DateFormat 会提供它的合适的具体子类的实例。这是一个简单工厂方法模式的绝佳应用。


 
比如说又一个农场公司,专门向市场销售各类水果。在这个系统里需要描述下列的水果:
葡萄 Grape
草莓 Strawberry
苹果 Apple
在这里自然想到了对象,所有的这些上述三种都属于水果,可实用的水果,那么我们就建立一个各种水果都适用的接口,以便与农场里的其他植物区分开。

水 果接口规定了所有的水果必须实现的接口,包括任何水果类必须具备的方法:种植plant(),生长grow()以及收获harvest(),从上面的类图 可以看出来,下面的三个水果都实现了接口中定义的方法。Applet类是水果的一种,但是又由于它是多年生植物,所以多了一个treeAge的性质。描述 苹果树的芳龄。代码其实和类图的表述是一样的。而Grape呢又得分为有籽和无籽两种,所以多给了一个seedless的性质。Strawberry也是 水果的一种。
农场的园丁也是系统的一部分,自然要由一个合适的类来代表。这个类就是FruitGardener类。其结构图如下:

在使用客户端的时候,客户端只需要调用FruitGardener的静态方法factory()就可以了。可以参看下面客户端的代码:
try{
    FruitGardener.factory(“grape“);
    FruitGardener.factory(“apple“);
    FruitGardener.factory(“strawberry“);
    ....
}catch(BadFruitException e) {
    ....
}

 

而如果接到不合法的要求,FruitGardener 类会抛出BadFruitException异常。

/*********************以下为农场的园丁类*********************/

public class FruitGardener

{

/**

* 静态工厂方法

*/

public static Fruit factory(String which) throws BadFruitException

if (which.equalsIgnoreCase("apple"))

{

return new Apple();

else if (which.equalsIgnoreCase("grape"))

{

return new Grape();

}

else

{

throw new BadFruitException("Bad fruit request");

}

}

}

 

/*********************以下为自定义异常类*********************/

public class BadFruitException extends Exception

{

public BadFruitException(String msg)

{

super(msg);

}

}

 

 

/*********************用户调用*********************/

………………

try

{

FruitGardener.factory("grape");

FruitGardener.factory("apple");

FruitGardener.factory("xxx");

}

catch(BadFruitException e)

{

...

}

…………………

 

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

 

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


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


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

 

另外一个例子(觉得更好理解:))

 

话说十年前,有一个暴发户,他家有三辆汽车——Benz奔驰、Bmw宝马、Audi奥迪,还雇了司机为他开车。不过,暴发户坐车时总是怪怪的:上Benz车后跟司机说开奔驰车!,坐上Bmw后他说开宝马车!,坐上Audi开奥迪车!。你一定说:这人有病!直接说开车不就行了?!
       
而当把这个暴发户的行为放到我们程序设计中来时,会发现这是一个普遍存在的现象。幸运的是,这种有病的现象在OO(面向对象)语言中可以避免了。下面就以Java语言为基础来引入我们本文的主题:工厂模式。

 

那么简单工厂模式怎么来使用呢?我们就以简单工厂模式来改造暴发户坐车的方式——现在暴发户只需要坐在车里对司机说句:开车就可以了。

//抽象产品角色

 public interface Car {

     void drive();

    }

//具体产品角色
 
public class Benz implements Car {

 

       @Override

       public void drive() {

              // TODO Auto-generated method stub

              System.out.println("Driving Benz");

       }

}

 

public class BMW  implements Car{

 

       @Override

       public void drive() {

              // TODO Auto-generated method stub

              System.out.println("Driving BMW");

       }

 }

。。。(奥迪我就不写了:P

//工厂类角色

public class Driver{

//工厂方法.注意 返回类型为抽象产品角色
   public static CardriverCar(String s)throws Exception    {

             //判断逻辑,返回具体的产品角色给Client
             if(s.equalsIgnoreCase("Benz"))  

                    return (Car)new Benz();

             else if(s.equalsIgnoreCase("Bmw"))

                    return (Car)new Bmw();

                    ......   
             elsethrow new Exception();

      。。。


//
欢迎暴发户出场......

public class Magnate{

      public static void main(String[] args){

             try{
                    //
告诉司机我今天坐奔驰              
                    Car car = Driver.driverCar("benz");
                    //
下命令:开车                   
                    car.drive();

 

   首先,使用了简单工厂模式后,我们的程序不在有病,更加符合现实中的情况;而且客户端免除了直接创建产品对象的责任,而仅仅负责消费产品(正如暴发户所为)。

      下面我们从开闭原则(对扩展开放;对修改封闭)上来分析下简单工厂模式。当暴发户增加了一辆车的时候,只要符合抽象产品制定的合同,那么只要通知工厂类知道就可以被客户使用了。所以对产品部分来说,它是符合开闭原则的;但是工厂部分好像不太理想,因为每增加一辆车,都要在工厂类中增加相应的业务逻辑或者判断 逻辑,这显然是违背开闭原则的。可想而知对于新产品的加入,工厂类是很被动的。对于这样的工厂类(在我们的例子中是为司机师傅),我们称它为全能类或者上帝类。
       我们举的例子是最简单的情况,而在实际应用中,很可能产品是一个多层次的树状结构。由于简单工厂模式中只有一个工厂类来对应这些产品,所以这可能会把我们的上帝累坏了,也累坏了我们这些程序员:(
      
于是工厂方法模式作为救世主出现了。

工厂方法模式

 

       工厂方法模式去掉了简单工厂模式中工厂方法的静态属性,使得它可以被子类继承。这样在简单工厂模式里集中在工厂方法上的压力可以由工厂方法模式里不同的工厂子类来分担。

你应该大致猜出了工厂方法模式的结构,来看下它的组成:

1)       抽象工厂角色: 这是工厂方法模式的核心,它与应用程序无关。是具体工厂角色必须实现的接口或者必须继承的父类。在java中它由抽象类或者接口来实现。

2)       具体工厂角色:它含有和具体业务逻辑有关的代码。由应用程序调用以创建对应的具体产品的对象。

3)       抽象产品角色:它是具体产品继承的父类或者是实现的接口。在java中一般有抽象类或者接口来实现。

4)       具体产品角色:具体工厂角色所创建的对象就是此角色的实例。在java中由具体的类来实现。

用类图来清晰的表示下的它们之间的关系:

Factory_method

Factory

工厂方法模式使用继承自抽象工厂角色的多个子类来代替简单工厂模式中的上帝类。正如上面所说,这样便分担了对象承受的压力;而且这样使得结构变得灵活起来——当有新的产品(即暴发户的汽车)产生时,只要按照抽象产品角色、抽象工厂角色提供的合同来生成,那么就可以被客户使用,而不必去修改任何已有的代码。可以看出工厂角色的结构也是符合开闭原则的!

       我 们还是老规矩,使用一个完整的例子来看看工厂模式各个角色之间是如何来协调的。话说暴发户生意越做越大,自己的爱车也越来越多。这可苦了那位司机师傅了,什么车它都要记得,维护,都要经过他来使用!于是暴发户同情他说:看你跟我这么多年的份上,以后你不用这么辛苦了,我给你分配几个人手,你只管管好他们就 行了!于是,工厂方法模式的管理出现了。代码如下:

//抽象产品角色,具体产品角色与简单工厂模式类似,只是变得复杂了些,这里略。
//
抽象工厂角色
public interface Driver{
       public Car driverCar();
}
public class BenzDriver implements Driver{
       public Car driverCar(){
             return new Benz();
       }
}
public class BmwDriver implements Driver{
       public Car driverCar()   {

returnnew Bmw();
       }
}

//应该和具体产品形成对应关系...
//
有请暴发户先生
 public class Magnate

{

             public static void main(String[] args)

             {

                    try{ 
                           Driver driver = new BenzDriver();

                           Car car = driver.driverCar();

                           car.drive();

                    }

       ……

}

可 以看出工厂方法的加入,使得对象的数量成倍增长。当产品种类非常多时,会出现大量的与之对应的工厂对象,这不是我们所希望的。因为如果不能避免这种情况,可以考虑使用简单工厂模式与工厂方法模式相结合的方式来减少工厂类:即对于产品树上类似的种类(一般是树的叶子中互为兄弟的)使用简单工厂模式来实现。

 

抽象工厂模式:

 abstract_Factory_UML

abstractfactory

 



最后这篇是转自板桥里人,最开始就是看他的文章,所以也拿来收藏.

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

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

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

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

Sample sample=newSample();

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

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

Sample sample=new Sample(参数);

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

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

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

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

Sample mysample=newMySample();
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.就使用

SamplesampleA=Factory.creator(1);

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

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

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

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

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

这里假设:Sample有两个concreteSampleASamleB,而Sample2也有两个concreteSample2ASampleB2

那么,我们就将上例中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
  }

}

 

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

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

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

举例

我们以JiveForumFactory为例,这个例子在前面的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 StringclassName = "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 : /n" + ne.getMessage());

    } catch (Exception se) {

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

    }

    return catDao;

  }

}


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

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

对于工厂模式,加上自己的理解:前面总结过command模式,我觉得command着重于参数的回调,也就是什么对象不在乎,只要能够做事情即可。所以接口-实现的方式比较直接简单。

工厂模式,主要是生产不同的产品,这些产品的可以有很多不同的内容。但是产品的超类是相通的,比如都是水果,都是汽车等等。工厂方法呢,就是产品过多的结果。(抽象工厂还没有看完。。。^_&

 

原创粉丝点击