工厂模式
一、概述
一般问题:在系统中,有时需要生产复杂对象,复杂性表现在:每次构造不同对象、对象的构造很复杂、对象的构造依赖具体环境等等。
核心方案:定义一个用于创建对象的接口,让其决定实例化哪个类。
设计意图:通常我们创建一个简单对象就是调用其new构造方法,同时也造成客户端和创建对象的耦合;如果是复杂对象,有必要对其解耦,工厂模式的初衷是封装复杂对象的new构造函数,实现客户端与复杂对象之间的解耦。
UML图如下:
Client和创建对象Product之间增加工厂Factory从而实现解耦。
一说到工厂模式,大家都会将其分为:简单工厂模式、工厂模式和抽象工厂模式,导致很多新人望而生畏。其实大可不必太纠结,它们只是实现手段和抽象层次的不同,其目的是一样的——解耦。
- 简单工厂模式——写法最直白,在工厂中定义静态方法,根据传入的参数不同,返回不同的产品子类,新增产品时需要修改代码(增加一个条件判断)。
- 工厂模式——为每种产品写一个对应的工厂子类,新增产品时,增加工厂子类,条件判断逻辑从Factory移到了Client,实际上依然要改代码。
- 抽象工厂模式——抽象层次更高,工厂子类由一个变成了多个(即工厂也有了工厂,反应在类图上就是Client和Factory之间又增加一层FactoryProducer),每一个子工厂负责生产一系列相关子产品组,所有产品组组成一个产品族。
并不是说抽象工厂模式就比工厂模式高级,区别只是适用场景不同,抽象工厂模式能处理产品种类更加复杂的情况。如果将抽象工厂模式的工厂子类减少到只剩一个,也就变成了工厂模式。
二、应用实战
Android系统应用有很多地方用到工厂模式,而且Android对设计模式的用法不是我们经常用到的标准写法,而是灵活运用。这也说明学设计模式学的是核心思想,而不应局限于实现方法。
以下是Android对工厂模式的用法:
/** * The factory of concrete presenters. */ public class PresenterFactory { private static final String TAG = "PresenterFactory"; private static final String PRESENTER_PACKAGE = "com.android.mms.ui."; //静态工厂方法,className可理解为依赖参数 public static Presenter getPresenter(String className, Context context, ViewInterface view, Model model) { try { if (className.indexOf(".") == -1) { className = PRESENTER_PACKAGE + className; } /*根据类名不同调用其构造方法生成实例*/ Class c = Class.forName(className); Constructor constructor = c.getConstructor( Context.class, ViewInterface.class, Model.class); return (Presenter) constructor.newInstance(context, view, model); } catch (ClassNotFoundException e) { Log.e(TAG, "Type not found: " + className, e); } catch (NoSuchMethodException e) { // Impossible to reach here. Log.e(TAG, "No such constructor.", e); } catch (InvocationTargetException e) { Log.e(TAG, "Unexpected InvocationTargetException", e); } catch (IllegalAccessException e) { Log.e(TAG, "Unexpected IllegalAccessException", e); } catch (InstantiationException e) { Log.e(TAG, "Unexpected InstantiationException", e); } return null; } }
很显然,这里采用了反射的方式来实现简单工厂模式,如此,简单工厂模式也符合“开闭原则”了,不需要在为增加产品而修改代码。
三、总结
总结:工厂模式是一种创建型设计模式,对客户端来讲,它是不同于new的一种新的创建对象的方法。
用一句话总结工厂模式:
你说个名字我就能造出来
优点:
- 客户端与复杂对象解耦,想创建一个对象,只要知道其名称就可以了
- 屏蔽产品的具体实现,调用者只关心产品的接口
缺点:
- 产品扩展困难,除反射方式外,一般需要修改代码
- 增加了系统复杂度