设计模式之工厂模式
1 概述
关于设计模式的内容等在网上有很多,但是没有自己实际的搞一把,多少对这些个东西还是有点模糊,今儿个从工厂模式开始,学习学习这23个由大牛们总结出来的设计模式,若有理解不到位的地方,还请多多指正。
工厂模式主要是为创建对象提供过渡接口,以便将创建对象的具体过程屏蔽隔离起来,达到提高灵活性的目的。
有人把工厂模式分为三类:
1)简单工厂模式(Simple Factory):不利于产生系列产品,只能生成单个产品。
2)工厂方法模式(Factory Method):可以生产多个产品。
3)抽象工厂模式(Abstract Factory):生产产品族,但不利于产生新的产品。
这三种模式从上到下逐步抽象,并且更具一般性。
在HeadFirst的《设计模式》书中将工厂模式分为两类:工厂方法模式(Factory Method)与抽象工厂模式(Abstract Factory)。将简单工厂模式(Simple Factory)看为工厂方法模式的一种特例,两者归为一类。
在此,以三种模式作为本文的实例说明对象。
2 简单工厂模式
简单工厂模式存在的目的很简单:定义一个用于创建对象的接口。
在简单工厂模式中,一个工厂类处于对产品类实例化调用的中心位置上,它决定那一个产品类应当被实例化。
组成结构:
1) 工厂类角色:这是本模式的核心,含有一定的判断逻辑,往往由一个具体类实现。
2) 抽象产品角色:它一般是具体产品继承的父类或者实现的接口,由接口或者抽象类来实现。
3) 具体产品角色:工厂类所创建的对象就是此角色的实例,由一个具体类实现。
3 工厂方法模式
工厂方法模式是简单工厂模式的进一步抽象化和推广,工厂方法模式里不再只由一个工厂类决定那一个产品类应当被实例化,这个决定被交给抽象工厂的子类去做。
组成结构:
1)抽象工厂角色: 这是工厂方法模式的核心,它与应用程序无关。是具体工厂角色必须实现的接口或者必须继承的父类。由抽象类或者接口来实现。
2)具体工厂角色:它含有和具体业务逻辑有关的代码。由应用程序调用以创建对应的具体产品的对象。
3)抽象产品角色:它是具体产品继承的父类或者是实现的接口。一般有抽象类或者接口来实现。
4)具体产品角色:具体工厂角色所创建的对象就是此角色的实例。由具体的类来实现。
4 简单工厂和工厂方法模式的比较
工厂方法模式和简单工厂模式在定义上的不同是很明显的。工厂方法模式的核心是一个抽象工厂类,而不像简单工厂模式, 把核心放在一个实类上。工厂方法模式可以允许很多实的工厂类从抽象工厂类继承下来, 从而可以在实际上成为多个简单工厂模式的综合,从而推广了简单工厂模式。
反过来讲,简单工厂模式是由工厂方法模式退化而来。设想如果我们非常确定一个系统只需要一个实的工厂类, 那么就不妨把抽象工厂类合并到实的工厂类中去。而这样一来,我们就退化到简单工厂模式了。
5 抽象工厂模式
抽象工厂有两个重要的概念:产品族和产品等级。
(1)产品族:指位于不同产品等级结构中,功能相关联的产品组成的家族。比如AMD的CPU和ADM芯片的主板,组成一个家族。Intel的CPU和Intel芯片的主板,又组成一个家族。而这两个家族都来自于两个产品等级:CPU,主板。
(2)产品等级:即某类产品的结构,一个等级结构是由相同的结构的产品组成。
在只有一个产品族的情况下,抽象工厂模式实际上退化到工厂方法模式。
6 示例
为了更好的理解文字的说明,下面来个例子,分别对照下上面的简单工厂、工厂、抽象工厂。我们的例子是用来造手机。
首先创建个手机的接口,包括打电话和发短信的功能:
1 package org.scott.factory.interf; 2 /** 3 * @author Scott 4 * @version 2013-11-11 5 * @description 6 */ 7 public interface Phone { 8 public void call(String phoneNumber); 9 public void sms(String phoneNumber, String smsContent); 10 }
创建其实现类,三星手机和苹果手机:
1 package org.scott.factory; 2 3 import org.scott.factory.interf.Phone; 4 5 /** 6 * @author Scott 7 * @version 2013-11-11 8 * @description 9 */ 10 public class ApplePhone implements Phone { 11 12 @Override 13 public void call(String phoneNumber) { 14 String str = "This is call from Apple phone to " + phoneNumber + "."; 15 System.out.println(str); 16 } 17 18 @Override 19 public void sms(String phoneNumber, String smsContent) { 20 String str = "This message is send from Apple phone to " + phoneNumber + ", the content is " + smsContent + "."; 21 System.out.println(str); 22 } 23 24 }
package org.scott.factory; import org.scott.factory.interf.Phone; /** * @author Scott * @version 2013-11-11 * @description */ public class SamsungPhone implements Phone { @Override public void call(String phoneNumber) { String str = "This is call from Samsung phone to " + phoneNumber + "."; System.out.println(str); } @Override public void sms(String phoneNumber, String smsContent) { String str = "This message is send from Samsung phone to " + phoneNumber + ", the content is " + smsContent + "."; System.out.println(str); } }
为了更好的管理生产手机的类型,我们定义一个枚举类型用来标记手机类型:
package org.scott.factory; /** * @author Scott * @version 2013-11-11 * @description */ public enum Type { Samsung("Samsung"), Apple("Apple"); private String type; private Type(String type){ this.type = type; } public String toString(){ return this.type; } public String getType() { return type; } public void setType(String type) { this.type = type; } }
万事俱备,下面来个造手机的简单工厂:
1 package org.scott.factory.simple_factory.factories; 2 3 import org.scott.factory.ApplePhone; 4 import org.scott.factory.Type; 5 import org.scott.factory.SamsungPhone; 6 import org.scott.factory.interf.Phone; 7 8 /** 9 * @author Scott 10 * @version 2013-11-11 11 * @description 12 */ 13 public class SimplePhoneFactory1 { 14 15 public Phone produce(String type){ 16 if(Type.Apple.toString().equals(type)){ 17 return new ApplePhone(); 18 }else if(Type.Samsung.toString().equals(type)){ 19 return new SamsungPhone(); 20 }else{ 21 System.out.println("Can not produce the special phone which type is " + type); 22 return null; 23 } 24 } 25 }
这种情况下,在客户端直接传入所需要生产的产品类型,进行生产:
SimplePhoneFactory1 factory = new SimplePhoneFactory1(); Phone myPhone = factory.produce(Type.Apple.toString()); myPhone.call("123456789"); myPhone.sms("123456789", "Just a test.");
当然,也可以这么来定义简单工厂,那就是在工厂中定好要生产的具体产品方法,由客户端直接调用就行:
1 package org.scott.factory.simple_factory.factories; 2 3 import org.scott.factory.ApplePhone; 4 import org.scott.factory.SamsungPhone; 5 import org.scott.factory.interf.Phone; 6 7 /** 8 * @author Scott 9 * @version 2013-11-11 10 * @description 11 */ 12 public class SimplePhoneFactory2 { 13 14 public Phone produceApplePhone(){ 15 return new ApplePhone(); 16 } 17 18 public Phone produceSamsungPhone(){ 19 return new SamsungPhone(); 20 } 21 }
这样的话,客户端直接调用对应的方法即可:
SimplePhoneFactory2 factory = new SimplePhoneFactory2(); Phone myPhone = factory.produceApplePhone(); myPhone.call("123456789"); myPhone.sms("123456789", "Just a test.");
继续,简单工厂也可以搞成静态的,多方便啊,连工厂对象都不用创建,直接调用:
1 package org.scott.factory.simple_factory.factories; 2 3 import org.scott.factory.ApplePhone; 4 import org.scott.factory.SamsungPhone; 5 import org.scott.factory.interf.Phone; 6 7 /** 8 * @author Scott 9 * @version 2013-11-11 10 * @description 11 */ 12 public class StaticPhoneFactory { 13 14 public static Phone produceApplePhone(){ 15 return new ApplePhone(); 16 } 17 18 public static Phone produceSamsungPhone(){ 19 return new SamsungPhone(); 20 } 21 }
对应的客户端调用代码:
Phone myPhone = StaticPhoneFactory.produceApplePhone(); myPhone.call("123456789"); myPhone.sms("123456789", "Just a test.");
简单工厂也就这么回事,来个分割线。
------------------------------------------------------------------------------------------------------------------
上面这种模式,类的创建依赖工厂类,如果想要拓展程序,必须对工厂类进行修改,这违背了闭包原则。怎么解决呢?
先定义个抽象接口:
1 package org.scott.factory.interf; 2 /** 3 * @author Scott 4 * @version 2013-11-11 5 * @description 6 */ 7 public interface Provider { 8 public Phone produce(); 9 }
然后让上面的手机工厂实现这个接口:
苹果工厂:
1 package org.scott.factory.normal_factory; 2 3 import org.scott.factory.ApplePhone; 4 import org.scott.factory.interf.Phone; 5 import org.scott.factory.interf.Provider; 6 7 /** 8 * @author Scott 9 * @version 2013-11-11 10 * @description 11 */ 12 public class ApplePhoneFactory implements Provider{ 13 14 @Override 15 public Phone produce() { 16 return new ApplePhone(); 17 } 18 }
三星工厂:
1 package org.scott.factory.normal_factory; 2 3 import org.scott.factory.SamsungPhone; 4 import org.scott.factory.interf.Phone; 5 import org.scott.factory.interf.Provider; 6 7 /** 8 * @author Scott 9 * @version 2013-11-11 10 * @description 11 */ 12 public class SamsungPhoneFactory implements Provider{ 13 14 @Override 15 public Phone produce() { 16 return new SamsungPhone(); 17 } 18 }
于是乎,客户端我们可以这样写:
1 Provider factory = new SamsungPhoneFactory(); 2 Phone myPhone = factory.produce(); 3 4 myPhone.call("987654321"); 5 myPhone.sms("9876454321", "Abstract...");
跟简单工厂模式相比,是不是方便了许多。这就是工厂模式,再来个分割线。
------------------------------------------------------------------------------------------------------------------
但是,上面这种工厂只能生产某中特定结构的产品,只能生产手机,却不能生产其余的东西。工厂要有市场思维,不能只做一个东西做到死,当工厂要扩展规模,要多生产几种产品的时候,咋办呢?上面的模式已经不能满足我们的需求了。又于是乎,抽象工厂模式来了。
再定义个接口,来扩展原有的接口,增加产品的类型:
package org.scott.factory.interf; /** * @author Scott * @version 2013-11-11 * @description */ public interface PhoneGift { public Phone producePhone(); public SIMCard produceShell(String shellType); }
这里,我们又生产了SIM卡,这才是真正的机卡合一!
定义SIM卡的抽象产品接口:
1 package org.scott.factory.interf; 2 /** 3 * @author Scott 4 * @version 2013-11-11 5 * @description 6 */ 7 public interface SIMCard { 8 public void welcomeMsg(String phoneNumber); 9 }
具体产品类:
中国移动的卡:
1 package org.scott.factory; 2 3 import org.scott.factory.interf.SIMCard; 4 5 /** 6 * @author Scott 7 * @version 2013-11-11 8 * @description 9 */ 10 public class CMCCCard implements SIMCard{ 11 12 @Override 13 public void welcomeMsg(String phoneNumber) { 14 System.out.println("中国移动欢迎你 " + phoneNumber); 15 } 16 17 }
中国电信的卡:
1 package org.scott.factory; 2 3 import org.scott.factory.interf.SIMCard; 4 5 /** 6 * @author Scott 7 * @version 2013-11-11 8 * @description 9 */ 10 public class CTCard implements SIMCard{ 11 12 @Override 13 public void welcomeMsg(String phoneNumber) { 14 System.out.println("中国电信欢迎你 " + phoneNumber); 15 } 16 }
再次于是乎,想生产各种定制版的机卡合一神器就变得简单了:
A版:
1 package org.scott.factory.abstract_factory; 2 3 import org.scott.factory.CMCCCard; 4 import org.scott.factory.SamsungPhone; 5 import org.scott.factory.interf.Phone; 6 import org.scott.factory.interf.PhoneGift; 7 import org.scott.factory.interf.SIMCard; 8 9 /** 10 * @author Scott 11 * @version 2013-11-12 12 * @description 13 */ 14 public class A implements PhoneGift{ 15 16 @Override 17 public Phone producePhone() { 18 return new SamsungPhone(); 19 } 20 21 @Override 22 public SIMCard produceShell(String shellType) { 23 return new CMCCCard(); 24 } 25 26 }
B版:
1 package org.scott.factory.abstract_factory; 2 3 import org.scott.factory.ApplePhone; 4 import org.scott.factory.CTCard; 5 import org.scott.factory.interf.Phone; 6 import org.scott.factory.interf.PhoneGift; 7 import org.scott.factory.interf.SIMCard; 8 9 /** 10 * @author Scott 11 * @version 2013-11-12 12 * @description 13 */ 14 public class B implements PhoneGift { 15 16 @Override 17 public Phone producePhone() { 18 return new ApplePhone(); 19 } 20 21 @Override 22 public SIMCard produceShell(String shellType) { 23 return new CTCard(); 24 } 25 26 }
还有其他版,你想怎么搞就随便了。
7 应用场景(来自网络)
工厂方法:
在以下情况下,适用于工厂方法模式:
(1) 当一个类不知道它所必须创建的对象的类的时候。
(2) 当一个类希望由它的子类来指定它所创建的对象的时候。
(3) 当类将创建对象的职责委托给多个帮助子类中的某一个,并且你希望将哪一个帮助子类是代理者这一信息局部化的时候。
抽象工厂:
(1) 一个系统不应当依赖于产品类实例如何被创建、组合和表达的细节,这对于所有形态的工厂模式都是重要的。
(2) 这个系统有多于一个的产品族,而系统只消费其中某一产品族。
(3) 同属于同一个产品族的产品是在一起使用的,这一约束必须在系统的设计中体现出来。
(4) 系统提供一个产品类的库,所有的产品以同样的接口出现,从而使客户端不依赖于实现。
8 总结
(1)简单工厂模式是由一个具体的类去创建其他类的实例,父类是相同的,父类是具体的。
(2)工厂方法模式是有一个抽象的父类定义公共接口,子类负责生成具体的对象,这样做的目的是将类的实例化操作延迟到子类中完成。
(3)抽象工厂模式针对的是有多个产品的等级结构,而工厂方法模式针对的是一个产品的等级结构。
注:理解不对的地方,欢迎一起交流学习~