设计模式学习之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 使一个类的实例化延迟到其子类。
- 适用性:
- 当一个类不知道它所必须创建的对象的类的时候。
- 当一个类希望由它的子类来指定它所创建的对象的时候。
- 当类将创建对象的职责委托给多个帮助子类中的某一个,并且你希望将哪一个帮助子类是代理者这一信息局部化的时候。
工厂方法模式中,最重要的一点是“让子类决定实例化那一个类”,这一点怎么才能做到呢,而且这样做有什么好处呢?
还是举例子最好了,对于工厂模式,能举的最好的例子当然是与生产产品有关的了,现在,我们假定生产一种手机,那么我们知道,每种手机都需要很多零部件,然后进行组装,打磨,包装等等,对于手机最主要的零件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。在这里,并不是采用工厂方法模式取代简单工厂,而是将简单工厂也应用到了工厂方法模式当中,因为这两者是不同的设计思想,也即 工厂方法模式是将创建过程抽象成接口 ,而 简单工厂是将创建过程隔离出来 。而抽象工厂模式则是这两种思想的进一步结合和升华。
- 抽象工厂模式:
提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。 - 适用性:
- 一个系统要独立于它的产品的创建、组合和表示时。
- 一个系统要由多个产品系列中的一个来配置时。
- 当你要强调一系列相关的产品对象的设计以便进行联合使用时。
- 当你提供一个产品类库,而只想显示它们的接口而不是实现时。
前面的例子虽然将不同的手机种类从判断分支中解脱了出来,但对于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的创建过程。
作者:everdom
出处:http://everdom.cnblogs.com/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文链接,否则保留追究法律责任的权利。