一.概述
平时作项目跟使用第三方类库的时候常常会用到工厂模式.什么是工厂模式,简单来讲就是他的字面意思.给外部批量提供相同或者不一样的产品,而外部不须要关心工厂是如何建立一个复杂产品的过程.因此工厂模式能够下降模块间的耦合,同时能够提升扩展性(当有新的产品出现时,只须要扩展工厂就好了,上层模块不敏感).html
工厂模式根据抽象的角度和层级的不一样能够分为两种模式:android
1.工厂方法模式 (Factory Method)ios
2.抽象工厂模式 (Abstract Factory)windows
二.实现
1.工厂方法模式缓存
工厂方法模式的特色是:iphone
一个抽象产品类(或接口),派生(或实现)多个真实产品类ide
一个抽象工厂类(或接口),派生(或实现)多个真实工厂类spa
通常来讲标准的工厂方法模式须要一个工厂只生产一种产品,那么当产品种类特别多的时候工厂的数量就特别多,因此一般会使用一些工厂方法模式的变种.设计
1.)标准工厂方法模式code
首先先介绍一下标准的工厂方法模式,不带任何的变种.以工厂生产不一样操做系统的手机为例.
创建一个产品接口,提供一个获取系统信息的方法.
?
1 2 3 4 5 6 |
/** * Created by jesse on 15-8-17. */ public interface IPhone { public void getOS(); } |
再根据IPhone接口实现Android,IOS,Blackberry三种手机.
?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
public class AndroidPhone implements IPhone { private final String TAG = AndroidPhone. class .getSimpleName(); @Override public void getOS() { Log.i(TAG, im Android); } } public class IosPhone implements IPhone { private final String TAG = IosPhone. class .getSimpleName(); @Override public void getOS() { Log.i(TAG, im IOS); } } public class BlackBerryPhone implements IPhone { private final String TAG = BlackBerryPhone. class .getSimpleName(); @Override public void getOS() { Log.i(TAG, im BlackBerry); } } |
标准的工厂方法模式都须要有抽象的工厂接口或者基类.
?
1 2 3 |
public abstract class IGenerator { public abstract IPhone generatePhone(String flag) throws Exception; } |
经过基类或者接口来实现真实的工厂类,这里须要注意跟简单工厂模式的不一样,标准的工厂方法模式里面一个工厂只生产一个产品,因此这里要根据产品的种类划分出来三个工厂,分别生产三种不一样的产品.这种设计思想很是契合单一职责原则.
?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
public class AndroidGenerator extends IGenerator { @Override public IPhone generatePhone() { return new AndroidPhone(); } } public class IOSGenerator extends IGenerator { @Override public IPhone generatePhone() { return new IosPhone(); } } public class BlackberryGenerator extends IGenerator { @Override public IPhone generatePhone() { return new BlackBerryPhone(); } } |
在客户端从工厂中得到产品并使用的过程当中都是经过接口进行访问的,在建立产品的阶段有效得下降了使用者和产品之间的耦合度.
?
1 2 3 4 5 6 7 8 9 10 11 |
IPhone android, ios, bb; IGenerator androidGenerator, iosGenerator, bbGenerator; androidGenerator = new AndroidGenerator(); iosGenerator = new IOSGenerator(); bbGenerator = new BlackberryGenerator(); android = androidGenerator.generatePhone(); ios = iosGenerator.generatePhone(); bb = bbGenerator.generatePhone(); android.getOS(); ios.getOS(); bb.getOS(); |
最终的运行效果显而易见.

2).简单工厂模式
接着分析一下简单工厂模式,这是最简单的变种,也叫作静态工厂方法模式,从这个名字就能够看出工厂的方法是静态的.既然工厂方法是静态的,那么工厂就不能经过继承进行扩展,若是有新增的产品,就只能在静态方法里面作修改因此从这个角度来讲简单工厂模式是不符合开闭原则的.
由于这是静态工厂方法模式,因此工厂类就没有接口或者虚基类来提供抽象.经过不一样的Flag来初始化不一样的产品.
?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
public class PhoneGenerator{ public final static String GENERATE_IOS = generate_ios; public final static String GENERATE_ANDROID = generate_android; public final static String GENERATE_BLACKBERRY = generate_blackberry; public static IPhone generatePhone(String flag) throws Exception { IPhone iPhone = null ; switch (flag){ case GENERATE_ANDROID: iPhone = new AndroidPhone(); break ; case GENERATE_IOS: iPhone = new IosPhone(); break ; case GENERATE_BLACKBERRY: iPhone = new BlackBerryPhone(); break ; default : throw new Exception(UNDEFINED FLAG); } return iPhone; } } |
对外部来讲要使用工厂只须要把目标产品类传过去就好了.运行结果跟1)中的是同样的.
?
1 2 3 4 5 6 7 |
IPhone android, ios, bb; android = PhoneGenerator.generatePhone(PhoneGenerator.GENERATE_ANDROID); ios = PhoneGenerator.generatePhone(PhoneGenerator.GENERATE_IOS); bb = PhoneGenerator.generatePhone(PhoneGenerator.GENERATE_BLACKBERRY); android.getOS(); ios.getOS(); bb.getOS(); |
3)结合反射的应用
假设须要加入一种搭载win10系统的手机,标准的工厂方法模式须要从新派生出来一个新的工厂来给客户使用,简单工厂模式也须要新增新的flag和case判断去构造新的手机.有没有什么方法能够尽可能避免这些修改呢?固然是有的,这里能够经过使用Class.forName 反射的方式来达到目的.
首先经过泛型来约束输入输出的参数类型,把异常抛到上层去处理并实现具体的工厂.
?
1 2 3 |
public abstract class IGenerator { public abstract <t extends = "" iphone= "" >T generatePhone(Class<t> clazz) throws Exception; }</t></t> |
?
1 2 3 4 5 6 7 |
public class PhoneGenerator extends IGenerator { public <t extends = "" iphone= "" >T generatePhone(Class<t> clazz) throws Exception { IPhone iPhone = null ; iPhone = (IPhone) Class.forName(clazz.getName()).newInstance(); return (T)iPhone; } }</t></t> |
经过这种装载的方式去初始化产品就能够达到上面描述的需求,能够根据需求直接添加一个实现了IPhone接口的WindowsPhone产品而不须要修改工厂,客户就能够直接从工厂拿到WindowsPhone的手机去使用了.
4)产品类私有构造应用
产品类私有构造应用其实更偏向与一种规范.既然使用工厂模式了,那就是这些手机所有都要在工厂内部建立出来.这种应用就作了限制,使用私有构造就不容许外部经过new的方式来建立,而工厂则经过反射和更改访问权限来建立产品.固然这个时候外部也能够经过一样的方式来建立对象,因此说这个应用更偏向于一种团队规范.
?
1 2 3 4 5 6 7 8 9 |
public class PhoneGenerator extends IGenerator { public <t extends = "" iphone= "" >T generatePhone(Class<t> clazz) throws Exception { IPhone iPhone = null ; Class phone = Class.forName(clazz.getName()); phone.getDeclaredConstructor().setAccessible( true ); iPhone = (IPhone) phone.newInstance(); return (T)iPhone; } }</t></t> |
5)缓存对象
对于那些建立起来特别消耗资源或者特别复杂的对象,可使用下面的方式来进行一个长期的缓存.对于那些有访问数量需求的对象也能够创建缓存List,经过设置最大建立数来控制对象量级的峰值.例如JDBC的最大链接数等.
?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
public class PhoneGenerator extends IGenerator{ private Map<string, iphone= "" > map = new HashMap<>(); @Override public <t extends = "" iphone= "" > T generatePhone(Class<t> clazz) throws Exception{ IPhone iPhone = null ; if (map.containsKey(clazz.getName())) iPhone = map.get(clazz.getName()); else { iPhone = (IPhone) Class.forName(clazz.getName()).newInstance(); map.put(clazz.getName(), iPhone); } return (T) iPhone; } }</t></t></string,> |
2.抽象工厂模式
抽象工厂模式的特色:
多个抽象产品类(或接口),派生(或实现)多个真实产品类
一个抽象工厂类(或接口),派生(或实现)多个真实工厂类
抽象工厂模式其实也算是工厂方法模式的一种延伸,在工厂方法模式中全部的产品都是一个系列的,都是从IPhone那里实现出的不一样真实产品,因此对于外部来讲他们都是手机产品,只须要关心手机的抽象接口就好了.然而又多个业务种类,而且这些业务有些依赖关系的时候,这种状况下使用的工厂模式就是抽象工厂模式.
接着在抽象方法模式里面的例子,在抽象工厂模式中咱们须要再多开一种产品,那就平板吧,而平板又分为Android平板和IOS平板(这里偷懒黑莓平板就不写了),而且平板跟手机有不少共同的地方,例如相同的OS硬件设计等.既然是一个新的产品线了,那么仍是先抽象出来平板的接口来.仍是老样子,打印一下本身是什么系统的.
?
1 2 3 |
public interface IPad { public void getBrand(); } |
接着经过IPad接口来实现两个不一样的平板.
?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
public class AndroidPad implements IPad { private final String TAG = AndroidPad. class .getSimpleName(); @Override public void getBrand() { Log.i(TAG, im Android pad); } } public class IOSPad implements IPad { private final String TAG = IOSPad. class .getSimpleName(); @Override public void getBrand() { Log.i(TAG, im IOS phone pad); } } |
在抽象工厂的接口的时候仍是继续使用泛型来建立了,这样也省的派生出来几个不一样的工厂.
?
1 2 3 4 |
public abstract class IGenerator { public abstract <t extends = "" iphone= "" >T generatePhone(Class<t> clazz) throws Exception; public abstract <t extends = "" ipad= "" >T generatePad(Class<t> clazz) throws Exception; }</t></t></t></t> |
?
1 2 3 4 5 6 7 8 9 10 11 12 13 |
public class ProductGenerator extends IGenerator{ @Override public <t extends = "" iphone= "" > T generatePhone(Class<t> clazz) throws Exception{ IPhone iPhone = (IPhone) Class.forName(clazz.getName()).newInstance(); return (T) iPhone; } @Override public <t extends = "" ipad= "" > T generatePad(Class<t> clazz) throws Exception { IPad iPad = (IPad) Class.forName(clazz.getName()).newInstance(); return (T) iPad; } }</t></t></t></t> |
假设有一个客户须要来我厂定制移动产品,A套餐中包含一个IOS手机和一个Android的平板,B套餐中包含一个Android手机和一个IOS平板.而这个套餐规则能够经过工厂进行约束.这样工厂就能胜任完成这个需求的任务了.
然而偏偏由于抽象工厂模式支持多种产品线,结果致使须要扩展一条新的产品的时候就会比较麻烦.假设须要新增一个屏幕贴膜产品,而且给每一个出厂的带屏幕的产品都配一个.那么要作的修改不只仅是要添加贴膜这个产品,还要修改从工厂的抽象到工厂的实现,还要修改工厂的约束.这是不符合开闭原则的.可是若是只是扩展一个产品的子系列,例如要新增一个windows平板,抽象工厂模式和工厂方法模式同样根本不须要修改工厂抽象和工厂实现,只须要新增产品就好了.