java design pattern (1. Factory Method pattern)

、设计模式Factory Method

此模式属于创建型设计模式,它只定义创建对象的接口,而由它的子类负责创建具体的对象,利用子类实例化不同的对象。图一是Factory Method 模式结构的类图(Class Diagram),其中:

  1. Product 定义了由factory method所创建对象的统一接口。
  2. ConcreteProduct 具体的类,实现Product接口。
  3. Creator 一般为抽象类,声明若干factory method(方法),由它创建类型为Product的对象。正因为它能"生产"对象,所以称为factory method。Creator也可能拥有一个方法创建某个缺省的具体对象。
  4. ConcreteCreator 重载factory method以创建某个 ConcreteProduct 的具体实例。

也就是说Creator依赖于ConcreteCreator创建Product型的ConcreteProduct对象。 Factory method使应用程序代码只需处理Product接口,而与具体的类(ConcreteProduct)无关,增强了代码可重用性,因为它独立于用户定义的具体的类。


 

4、Factory Method在Javamail中的应用


图二:Factory Method 例  

图二指示了Factory Method在Javamail中的应用。其中的类Store相当于图一中的Creator,Store的两个子类Pop3Store,IMAPStore相当于图一中的ConcreteStore,类Folder相当于Product,Folder的两个子类Pop3Folder和IMAPFolder相当于ConcreteProduct,而Store中的方法getFolder就是一个factory method,由子类Pop3Store实例化Pop3Folder,由IMAPStore实例化IMAPFolder。

类似的图二中的Folder相对于Store来说是Product,但相对于Messsage却也是一个Creator,其方法getMessage同样也是factory method。

4.1可重用性


 

上表是使用Factory Method模式创建对象和直接创建对象的比较,显然前者对于创建不同的对象所用的代码几乎相同,便于代码重用,而后者对于创建不同的对象所用代码就相差很大,想做改动就比较麻烦,若想重用就几乎是不可能的。设计可重用的面向对象软件是十分不易的,恰当地运用设计模式则可在一定程度上解决这个问题。

4.2 可扩展性

如有一种对应与Pop3、IMAP的新的邮件协议NewP,则很容易使系统支持这种新的协议,扩展Store建立新类NewPStore,扩展Folder建立新类NewPFolder,扩展Message建立新类NewPMessage,就建立起了新协议的大致框架。

5、Parameterized Factory Method在Javamail中的应用

Factroy Method设计模式还有一个变异Parameterized factory method模式。对于Parameterized factory method模式,其factory method有一参数,用于指明需创建的对象的类型,这样一个类的factory method可以创建多种具体类型(ConcreteProduct)的对象,与Factory Method相同的是它所创建的对象都具有同样的接口Product。

在Javamail中有一个final static类Session,不能创建它的子类,通过此类设置和访问一些特殊的属性,另外此类还拥有若干Parameterized Factory Method,可以创建多种对象。

5.1可重用性

图三中的getStore和getTransport都是Parameterized Factory Method,以getStore为例,给参数以不同的值就能创建不同的对象。例如:

Session session = Session.getDefaultInstance(props, authenticator);
Store store1 = session.getStore("pop3");  //实例化Pop3Store
Store store2 = session.getStore("IMAP");  //实例化IMAPStore

如上代码所示,通过给出不同的参数,即能实例化不同的对象,其代码重用是相当简单的。

5.2可扩展性

若你新建了一个邮件系统,拥有Store的特殊子类NewPStore,此类采用特殊的协议NewP,并已进行了相关设置,如在javamail.default.providers文件中设置了:

protocol = NewP; type = store; class = com.sun.mail.IMAP.NewPStore;
这样你就可以利用
Session session = Session.getDefaultInstance(props, authenticator);
Store store = session.getStore("NewP");  //实例化NewStore

创建一个NewPStore型对象。


 

6、总结

Factory Method及其变异Parameterized factory method 都是极为常用的设计模式。在Javamail中还有许多地方使用了Factory Method或Parameterized Factory Method,比如类Session中的方法getProvider、getFolder也是Parameterized Factory Method, getInstance也可以算是Parameterized Factory Method,不过它比较特殊,它实例化Session自身。而且两者都很容易实现代码重用,进行系统扩展。

但Factory Method模式和Parameterized factory method略有不同。对于Factory Method,有一个潜在的不利因素,那就是为了创建一个特殊的对象,必须有相应于Creator的一个子类。如上所述创建一个Pop3Folder对象必须要有一个Store的子类Pop3Store, 创建一个IMAPFolder对象必须要有一个Store的子类IMAPStore。 而对于Parameterized factory method只需要一个具体的Creator,提供不同的参数就能创建不同的对象。所以可视实际情况在这两者之中取舍。一般原则是:当创建一个具体的Product时确实需要扩展Creator,那么就采用Factory Method,如Pop3和IMAP是两者区别比较大的不同的邮件协议,Pop3Store和IMAPStore差异很大,创建Pop3Folder确实需要有一个特殊的Store扩展--Pop3Store,对于IMAP也一样,此时就需要采用Factory Method模式。而为了创建Pop3Store、IMAPStore,如还是采用Factory Method模式而分别创建了Session的子类Pop3Session、IMAPSession,其意义就不是很大,完全是为了创建Pop3Store而创建Pop3Session,Session、Pop3Session、IMAPSession三者代码几乎相同,可以说是一种代码冗余。



--------------------------------------------------------------------------------------------

Reference 2:

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

为何使用?
工厂模式是我们最常用的模式了,著名的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 : \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变量就可以。

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

--------------------------------

reference 3:

  工厂模式就是专门负责将大量有共同接口的类实例化,而且不必事先知道每次是要实例化哪一个类的模式。它定义一个用于创建对象的接口,由子类决定实例化哪一个类。
2 . 简单工厂模式的结构 
 https://i-blog.csdnimg.cn/blog_migrate/d4323c8156b71b0675d08c42ac1327b4.gif

3. 一个简单例子
java 代码
  1. // 产品接口         
  2. public interface Product {   
  3.   
  4.     public void getName();   
  5.   
  6. }   
  7.   
  8. // 具体产品A   
  9. public class ProductA implements Product {   
  10.   
  11.     public void getName() {   
  12.         System.out.println("  I am ProductA  ");   
  13.     }   
  14.   
  15. }   
  16.   
  17. // 具体产品B   
  18. public class ProductB implements Product {   
  19.   
  20.     public void getName() {   
  21.         System.out.println("  I am ProductB  ");   
  22.     }   
  23.   
  24. }   
  25.   
  26. // 工厂类   
  27. public class ProductCreator {   
  28.   
  29.     public Product createProduct(String type) {   
  30.         if (" A ".equals(type)) {   
  31.             return new ProductA();   
  32.         }   
  33.         if (" B ".equals(type)) {   
  34.             return new ProductB();   
  35.         } else  
  36.             return null;   
  37.     }   
  38.   
  39.     public static void main(String[] args) {   
  40.         ProductCreator creator = new ProductCreator();   
  41.         creator.createProduct(" A ").getName();   
  42.         creator.createProduct(" B ").getName();   
  43.     }   
  44. }  
4. 小结工厂模式的适用范围 
• 在编码时不能预见需要创建哪一种类的实例。 
• 一个类使用它的子类来创建对象。 
• 开发人员不希望创建了哪个类的实例以及如何创建实例的信息暴露给外部程序。  

 

抽象工厂模式 

1. 抽象工厂模式可以说是简单工厂模式的扩展,它们主要的区别在于需要创建对象的复杂程度上。 
在抽象工厂模式中,抽象产品可能是一个或多个,从而构成一个或多个产品族。 在只有一个产品族的情况下,抽象工厂模式实际上退化到工厂方法模式。 
2. 抽象工厂模式的结构 

 https://i-blog.csdnimg.cn/blog_migrate/784869f71398b122971e48cf1509f842.gif

3. 一个简单例子

java 代码
  1. //  产品 Plant接口         
  2. public interface Plant {   
  3. }   
  4.   
  5. // 具体产品PlantA,PlantB   
  6. public class PlantA implements Plant {   
  7.   
  8.     public PlantA() {   
  9.         System.out.println(" create PlantA ! ");   
  10.     }   
  11.   
  12.     public void doSomething() {   
  13.         System.out.println("  PlantA do something  ");   
  14.     }   
  15. }   
  16.   
  17. public class PlantB implements Plant {   
  18.     public PlantB() {   
  19.         System.out.println(" create PlantB ! ");   
  20.     }   
  21.   
  22.     public void doSomething() {   
  23.         System.out.println("  PlantB do something  ");   
  24.     }   
  25. }   
  26.   
  27. // 产品 Fruit接口   
  28. public interface Fruit {   
  29. }   
  30.   
  31. // 具体产品FruitA,FruitB   
  32. public class FruitA implements Fruit {   
  33.     public FruitA() {   
  34.         System.out.println(" create FruitA ! ");   
  35.     }   
  36.   
  37.     public void doSomething() {   
  38.         System.out.println("  FruitA do something  ");   
  39.     }   
  40. }   
  41.   
  42. public class FruitB implements Fruit {   
  43.     public FruitB() {   
  44.         System.out.println(" create FruitB ! ");   
  45.     }   
  46.   
  47.     public void doSomething() {   
  48.         System.out.println("  FruitB do something  ");   
  49.     }   
  50. }   
  51.   
  52. // 抽象工厂方法   
  53. public interface AbstractFactory {   
  54.     public Plant createPlant();   
  55.   
  56.     public Fruit createFruit();   
  57. }   
  58.   
  59. // 具体工厂方法   
  60. public class FactoryA implements AbstractFactory {   
  61.     public Plant createPlant() {   
  62.         return new PlantA();   
  63.     }   
  64.   
  65.     public Fruit createFruit() {   
  66.         return new FruitA();   
  67.     }   
  68. }   
  69.   
  70. public class FactoryB implements AbstractFactory {   
  71.     public Plant createPlant() {   
  72.         return new PlantB();   
  73.     }   
  74.   
  75.     public Fruit createFruit() {   
  76.         return new FruitB();   
  77.     }   
  78. }  
4. 小结 
在以下情况下,应当考虑使用抽象工厂模式。 
  首先,一个系统应当不依赖于产品类实例被创立,组成,和表示的细节。这对于所有形态的工厂模式都是重要的。 
  其次,这个系统的产品有多于一个的产品族。 
  第三,同属于同一个产品族的产品是设计成在一起使用的。这一约束必须得在系统的设计中体现出来。 
  最后,不同的产品以一系列的接口的面貌出现,从而使系统不依赖于接口实现的细节。 
  其中第二丶第三个条件是我们选用抽象工厂模式而非其它形态的工厂模式的关键性条件。



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值