设计模式学习之Factory Method模式和Abstract Factory模式

终于到了工厂模式了,说起工厂模式,不得不把工厂方法模式和抽象工厂模式结合起来说,这两种模式都有工厂,乍听起来还真容易混淆,但它们却是不相同的两种模式,但又互相有联系。那么这两者之间各有什么用途,互相之间又有什么联系呢?一个一个来吧。
既然说起了工厂模式,那么首先有一点是需要搞清的,那就是,所有的工厂模式都是为了将对象的创建过程封装起来,要么 将创建过程隔离出来 ,要么 将创建过程抽象成接口
那么,在说这两种模式之前,先说一下简单工厂,所谓的简单工厂,其实就是一种最简单的将创建过程隔离的设计方法。我们通常在创建一个对象时,都会用到如下语句:

Object o=new Object();

即new一个对象,但这种做法是最最基本最最原始的创建对象的方法,在面向对象的设计中,我们必须要遵循一个原则: 要依赖抽象,不要依赖具体类 。所以,这种做法因为太过具体,所以我们得想办法把它抽象出来,怎么抽象呢,用简单工厂可以这样实现:

 1  public class Client
 2   {
 3     public void do()
 4     {
 5       Object o=SimpleFactory.createObject();
 6     }
 7   }
 8   public class SimpleFactory
 9   {
10     public static Object createObject()
11     {
12        Object o=new Object();
13        return o;
14     }
15   }

咋看起来变化不大,不就是吧new Object改为一个类的静态方法了吗(其实也可以用非静态方法,不过需要先实例化对象),但这样做却可以适应需求的改变,比如说,我现在想要根据不同的类型创建不同的对象,如果直接用new方法,则会在do方法里面写上很多歌new Object()的语句,而且还有很多的判断,每当新增加一种类型,就得改动Client类代码。而如果将创建对象的过程用简单工厂封装起来,则只需要改动简单工厂里面的代码:

 1   //直接用new方法
 2   public class Client
 3   {
 4     public void do(String type)
 5     {
 6        Object o;
 7        switch(type)
 8        {
 9          case "type1":o=new Object1();break;//Object1、Object2都是Object的子类
10          case "type2":o=new Object2();break;
11          //...
12        }
13     }
14   }
15 
16   //采用简单工厂封装创建过程
17   public class Client
18   {
19     public void do(String type)
20     {
21       Object o=SimpleFactory.createObject(type);
22     }
23   }
24   public class SimpleFactory
25   {
26     public static Object createObject(String type)
27     {
28        Object o;
29        switch(type)
30        {
31          case "type1":o=new Object1();break;//Object1、Object2都是Object的子类
32          case "type2":o=new Object2();break;
33          //...
34        }
35        return o;     
36     }
37   }

但这样做还是会带来不便,因为还是要改动代码,增加新的判断分支,怎样才能更好的封装以增强可维护性和可扩展性呢,工厂方法模式和抽象工厂模式就可以大大增强简单工厂的作用。

 

那么首先说说工厂方法模式,先说基本概念吧

  • 工厂方法模式:
    定义一个用于创建对象的接口,让子类决定实例化哪一个类。Factory Method 使一个类的实例化延迟到其子类。
  • 适用性:
    1. 当一个类不知道它所必须创建的对象的类的时候。
    2. 当一个类希望由它的子类来指定它所创建的对象的时候。
    3. 当类将创建对象的职责委托给多个帮助子类中的某一个,并且你希望将哪一个帮助子类是代理者这一信息局部化的时候。

工厂方法模式中,最重要的一点是“让子类决定实例化那一个类”,这一点怎么才能做到呢,而且这样做有什么好处呢?
还是举例子最好了,对于工厂模式,能举的最好的例子当然是与生产产品有关的了,现在,我们假定生产一种手机,那么我们知道,每种手机都需要很多零部件,然后进行组装,打磨,包装等等,对于手机最主要的零件cpu来说,就有不同类型的cpu,比如高通,intel,苹果,德州仪器,或者mtk等等,那么我们在生产手机时,就需要使用某种芯片,就相当于创建这些对象。但不同手机可以使用不同的芯片,如果采用简单工厂来生产这些芯片,就像下面一样:

 1  public class Cellphone
 2   {
 3     Cpu cpu;
 4     Memory memory;
 5     Screen screen;
 6     public void getCpu();
 7     public void makePhone(String phoneType)
 8     {
 9       switch(phoneType)
10       {
11         case "iphone4s":cpu=CpuFactory.createCpu("apple A5");break;
12         case "三星i9100":cpu=CpuFatory.createCpu("高通");break;
13         case "摩托罗拉me525":cpu=CpuFactory.createCpu("高通");break;
14         case "华为8500":cpu=CpuFactory.createCpu("德州仪器");break;
15         case "诺基亚":cpu=CpuFactory.createCpu("intel");break;
16         //...
17       }
18     }
19     public void package();
20   }
21 
22   public class CpuFactory
23   {
24     public static Cpu createCpu(String cpuType)
25     {
26       Cpu cpu;
27       switch(cpuType)
28       {
29         case "apple A5":cpu=new AppleCpu("A5");break;
30         case "apple A5X":cpu=new AppleCpu("A5X");break;
31         case "高通1代":cpu=new QualcommCpu("1");break;
32         case "高通2代":cpu=new QualcommCpu("2");break;
33         case "德州仪器":cpu=new TiCpu();break;
34         case "intel":cpu=new IntelCpu();break;
35         //...
36       }
37     }
38   }

那么,如果增加新的cpu,就要在CpuFactory里面再增加新的判断分支。而如果使用工厂方法模式,就可以这样:

 1   public class Cellphone
 2   {
 3     protected String type;
 4     protected Cpu cpu;
 5     protected Memory memory;
 6     protected Screen screen;
 7     protected abstract Cpu getCpu();
 8     public Cellphone(String type)
 9     {
10       this.type=type;
11     }
12     public void makePhone()
13     {
14       cpu=getCpu();
15       //..
16     }
17     public void package();
18   }
19 
20   public class Iphone extends Cellphone
21   {
22      public Cpu getCpu()
23      {
24         switch(type)
25         {
26           case "3gs":cpu=CpuFactory.createCpu("apple A3");
27           case "4":cpu=CpuFactory.createCpu("apple A5");
28           case "4s":cpu=CpuFactory.createCpu("apple A5X");
29           case "5":cpu=CpuFactory.createCpu("apple A6");
30           //...
31         }
32      }
33   }
34   public class Motorola extends Cellphone
35   {
36     public Cpu getCpu()
37     {
38       switch(type)
39         {
40           case "millstone":cpu=CpuFactory.createCpu("高通1代");break;
41           case "me525":cpu=CpuFactory.createCpu("高通2代");break;
42           case "millstone2":cpu=CpuFactory.createCpu("高通2代");break;
43           case "xt860":cpu=CpuFactory.createCpu("高通3代");break;
44           //...
45         }
46     }
47   }
48   //...
49   public class CpuFactory
50   {
51     public static Cpu createCpu(String cpuType)
52     {
53       Cpu cpu;
54       switch(cpuType)
55       {
56         case "apple A5":cpu=new AppleCpu("A5");break;
57         case "apple A5X":cpu=new AppleCpu("A5X");break;
58         case "高通1代":cpu=new QualcommCpu("1");break;
59         case "高通2代":cpu=new QualcommCpu("2");break;
60         case "德州仪器":cpu=new TiCpu();break;
61         case "intel":cpu=new IntelCpu();break;
62         //...
63       }
64     }
65   }

对于上例来说,我们将不同类型的手机分离了出来,让它们共同继承Cellphone基类,显得结构清晰,便于扩展,比如现在如果有一款三星的手机,就只需要继承新建一个Samsung类然后继承Cellphone类,然后重写getCpu方法即可实现三星手机的cpu采购过程。也就是说,getCpu作为一个抽象的创建cpu的方法,让子类去具体实现。
像这种结构,就体现了“具体的手机产品需要具体的cpu类型”的设计。也就是“定义一个用于创建对象的接口(getCpu),让子类决定实例化哪一个类”,在Iphone类和Motorola类中分别实现了getCpu方法,使用简单工厂产生了各种的cpu。在这里,并不是采用工厂方法模式取代简单工厂,而是将简单工厂也应用到了工厂方法模式当中,因为这两者是不同的设计思想,也即 工厂方法模式是将创建过程抽象成接口 ,而 简单工厂是将创建过程隔离出来 。而抽象工厂模式则是这两种思想的进一步结合和升华。

  • 抽象工厂模式:
    提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。
  • 适用性:
    1. 一个系统要独立于它的产品的创建、组合和表示时。
    2. 一个系统要由多个产品系列中的一个来配置时。
    3. 当你要强调一系列相关的产品对象的设计以便进行联合使用时。
    4. 当你提供一个产品类库,而只想显示它们的接口而不是实现时。

前面的例子虽然将不同的手机种类从判断分支中解脱了出来,但对于cpu的制作仍然显得比较乱,也就是说CpuFactory承担了所有的cpu制作任务(搞垄断啊:>),而cpu的型号五花八门,同一种型号的cpu也有很多的版本,如果放到一个工厂生产,实在不是一个聪明的做法,所以,我们自然考虑将不同的cpu从一个简单工厂解脱出来,分别由不同的工厂来生产(这样才有竞争嘛:>),因此,我们将简单工厂改造成抽象工厂,怎么改造?看下面:

 1   //抽象工厂类
 2   public class CpuFactory
 3   {
 4     Cpu cpu;
 5     public abstract Cpu createCpu(String type);
 6   }
 7   //苹果cpu工厂类
 8   public class AppleCpuFactory extends CpuFactory
 9   {
10     public Cpu createCpu(String type)
11     {
12       switch(type)
13       {
14         case "A3":this.cpu=new AppleCpu("A3");break;
15         case "A5":this.cpu=new AppleCpu("A5");break;
16         case "A5X":this.cpu=new AppleCpu("A5X");break;
17         case "A6":this.cpu=new AppleCpu("A6");break;
18         //...
19       }
20     }
21   }
22   //高通cpu工厂类
23   public class QualcommCpuFactory extends CpuFactory
24   {
25     public Cpu createCpu(String type)
26     {
27       switch(type)
28       {
29         case "1代":this.cpu=new QualcommCpu("1");break;
30         case "2代":this.cpu=new QualcommCpu("2");break;
31         case "3代":this.cpu=new QualcommCpu("3");break;
32         //...
33       }
34     }
35   }
36   //德州仪器工厂类
37   public class TiCpuFactory extends CpuFactory
38   {
39     public Cpu createCpu(String type)
40     {
41       switch(type)
42       {
43         case "1代":this.cpu=new TiCpu("1");break;
44         case "2代":this.cpu=new TiCpu("2");break;
45         case "3代":this.cpu=new TiCpu("3");break;
46         //...
47       }
48     }
49   }
50   //cpu产品抽象类
51   public class Cpu
52   {
53     protected float frequency;
54     protected String type;
55     public Cpu(String type)
56     {
57       this.type=type;
58     }
59   }
60   //苹果cpu产品类
61   public class AppleCpu extends Cpu
62   {
63   }
64   //高通cpu产品类
65   public class QualcommCpu extends Cpu
66   {
67   }
68   //德州仪器cpu产品类
69   public class TiCpu extends Cpu
70   {
71   }

使用这种模式,就将各种具体的工厂分出了清晰的结构,也就是将工厂也抽象成接口了,这就是为什么叫“抽象工厂”的原因。
既然我们升级简单工厂为抽象工厂,那么创造产品自然就要用新的接口方法了:

 1  public class Cellphone
 2   {
 3     protected String type;
 4     protected Cpu cpu;
 5     protected Memory memory;
 6     protected Screen screen;
 7     protected CpuFactory cpuFactory;
 8     protected abstract Cpu getCpu();
 9     public Cellphone(String type)
10     {
11       this.type=type;
12     }
13     public void makePhone()
14     {
15       cpu=getCpu();
16       //..
17     }
18     public void package(){};
19   }
20   public class Iphone extends Cellphone
21   {
22      public Cpu getCpu()
23      {
24         this.cpuFactory=new AppleCpuFactory();//由于创建产品的方法不再是静态方法,所以需要创建实例
25         switch(type)
26         {
27           case "3gs":cpu=this.cpuFactory.createCpu("A3");
28           case "4":cpu=this.cpuFactory.createCpu("A5");
29           case "4s":cpu=this.cpuFactory.createCpu("A5X");
30           case "5":cpu=this.cpuFactory.createCpu("A6");
31           //...
32         }
33      }
34   }
35   public class Motorola extends Cellphone
36   {
37     public Cpu getCpu()
38     {
39       this.cpuFactory=new QualcommCpuFactory();
40       switch(type)
41         {
42           case "millstone":cpu=this.cpuFactory.createCpu("1");break;
43           case "me525":cpu=this.cpuFactory.createCpu("2");break;
44           case "millstone2":cpu=this.cpuFactory.createCpu("2");break;
45           case "xt860":cpu=this.cpuFactory.createCpu("3");break;
46           //...
47         }
48     }
49   }

这样一来,整个结构就清晰多了,用一句话概括就是:由具体的手机使用具体的工厂生产具体的cpu,但手机、工厂和cpu都通过接口进行交互,较好的封装了各自的实现,从而达到了较好的可扩展性,大大降低了各自的耦合性。现在,如果要添加新的手机、新的cpu,就可以建立新类型的工厂来实现对新的cpu的创建过程。

posted @ 2012-07-27 11:36  everdom  阅读(601)  评论(0编辑  收藏  举报