首先来解决一个常见的疑问:可能有朋友会认为,上面示例中的简单工厂看起来不就是把客户端里面的“new Impl()”移动到简单工厂里面吗?不仍是同样经过new一个实现类来获得接口吗?把“new Impl()”这句话放到客户端和放到简单工厂里面有什么不一样吗?
理解这个问题的重点就在于理解简单工厂所处的位置。
根据前面的学习,咱们知道接口是用来封装隔离具体的实现的,目标就是不要让客户端知道封装体内部的具体实现。简单工厂的位置是位于封装体内的,也就是简单工厂是跟接口和具体的实如今一块儿的,算是封装体内部的一个类,因此简单工厂知道具体的实现类是没有关系的。整理一下简单工厂的结构图,新的图如图7所示:
图7 整理后的简单工厂结构
html
图7中虚线框,就比如是一个组件的包装边界,表示接口、实现类和工厂类组合成了一个组件,在这个封装体里面,只有接口和工厂是对外的,也就是让外部知道并使用的,因此故意漏了一些在虚线框外,而具体的实现类是不对外的,被彻底包含在虚线框内。
对于客户端而言,只是知道了接口Api和简单工厂Factory,经过Factory就能够得到Api了,这样就达到了让Client在不知道具体实现类的状况下获取接口Api。
因此看似简单的把“new Impl()”这句话从客户端里面移动到了简单工厂里面,实际上是有了质的变化的。java
(1)简单工厂的功能
工厂嘛,就是用来造东西的。在Java里面,一般状况下是用来造接口的,可是也能够造抽象类,甚至是一个具体的类实例。
必定要注意,虽然前面的示例是利用简单工厂来建立的接口,可是也是能够用简单工厂来建立抽象类或者是普通类的实例的。
(2)静态工厂
使用简单工厂的时候,一般不用建立简单工厂类的类实例,没有建立实例的必要。所以能够把简单工厂类实现成一个工具类,直接使用静态方法就能够了,也就是说简单工厂的方法一般都是静态的,因此也被称为静态工厂。若是要防止客户端无谓的创造简单工厂实例,还能够把简单工厂的构造方法私有化了。
(3)万能工厂
一个简单工厂能够包含不少用来构造东西的方法,这些方法能够创造不一样的接口、抽象类或者是类实例,一个简单工厂理论上能够构造任何东西,因此又称之为“万能工厂”。
虽然上面的实例中,在简单工厂里面只有一个方法,但事实上,是能够有不少这样建立方法的,这点要注意。
(4)简单工厂建立对象的范围
虽然从理论上讲,简单工厂什么都能造,但对于简单工厂可建立对象的范围,一般不要太大,建议控制在一个独立的组件级别或者一个模块级别,也就是一个组件或模块一个简单工厂。不然这个简单工厂类会职责不明,有点大杂烩的感受。
(5)简单工厂的调用顺序示意图
简单工厂的调用顺序如图8所示:
图8 简单工厂的调用顺序示意图数据库
(6)简单工厂命名的建议编程
虽说简单工厂的方法可能是用来造接口的,可是仔细分析就会发现,真正能实现功能的是具体的实现类,这些实现类是已经作好的,并非真的靠简单工厂来创造出来的,简单工厂的方法无外乎就是:实现了选择一个合适的实现类来使用。
因此简单工厂方法的内部主要实现的功能是“选择合适的实现类”来建立实例对象。既然要实现选择,那么就须要选择的条件或者是选择的参数,选择条件或者是参数的来源一般又有几种:设计模式
下面来看个示例,看看由客户端来传入参数,如何写简单工厂中的方法。
(1)在刚才的示例上再添加一个实现,称为Impl2,示例代码以下:api
/** * 对接口的一种实现 */ public class Impl2 implements Api{ public void test1(String s) { System.out.println("Now In Impl The input s=="+s); } }
(2)如今对Api这个接口,有了两种实现,那么工厂类该怎么办呢?到底如何选择呢?不可能两个同时使用吧,看看新的工厂类,示例代码以下:缓存
/** * 工厂类,用来创造Api的 */ public class Factory { /** * 具体的创造Api的方法,根据客户端的参数来建立接口 * @param type 客户端传入的选择创造接口的条件 * @return 创造好的Api对象 */ public static Api createApi(int type){ //这里的type也能够不禁外部传入,而是直接读取配置文件来获取 //为了把注意力放在模式自己上,这里就不去写读取配置文件的代码了 //根据type来进行选择,固然这里的1和2应该作成常量 Api api = null; if(type==1){ api = new Impl(); }else if(type==2){ api = new Impl2(); } return api; } }
(3)客户端没有什么变化,只是在调用Factory的createApi方法的时候须要传入参数,示例代码以下:工具
public class Client { public static void main(String[] args) { //注意这里传递的参数,修改参数就能够修改行为,试试看吧 Api api = Factory.createApi(2); api.test1("哈哈,没关系张,只是个测试而已!"); } }
(4)要注意这种方法有一个缺点
因为是从客户端在调用工厂的时候,传入选择的参数,这就说明客户端必须知道每一个参数的含义,也须要理解每一个参数对应的功能处理。这就要求必须在必定程度上,向客户暴露必定的内部实现细节。学习
如今已经学会经过简单工厂来选择具体的实现类了,但是还有问题。好比:在如今的实现中,再新增长一种实现,会怎样呢?
那就须要修改工厂类,才能把新的实现添加到现有系统中。好比如今新加了一个实现Impl3,那么须要相似下面这样来修改工厂类:测试
public class Factory { public static Api createApi(int type){ Api api = null; if(type==1){ api = new Impl(); }else if(type==2){ api = new Impl2(); } else if(type==3){ api = new Impl3(); } return api; } }
每次新增长一个实现类都来修改工厂类的实现,确定不是一个好的实现方式。那么如今但愿新增长了实现类事后不修改工厂类,该怎么办呢?
一个解决的方法就是使用配置文件,当有了新的实现类事后,只要在配置文件里面配置上新的实现类就行了,在简单工厂的方法里面可使用反射,固然也可使用IoC/DI(控制反转/依赖注入,这个不在这里讨论)来实现。
看看如何使用反射加上配置文件,来实现添加新的实现类事后,无须修改代码,就能把这个新的实现类加入应用中。
(1)配置文件用最简单的properties文件,实际开发中可能是xml配置。定义一个名称为“FactoryTest.properties”的配置文件,放置到Factory同一个包下面,内容以下:
ImplClass=cn.javass.dp.simplefactory.example5.Impl
若是新添加了实现类,修改这里的配置就能够了,就不须要修改程序了。
(2)此时的工厂类实现以下:
/** * 工厂类,用来创造Api对象 */ public class Factory { /** * 具体的创造Api的方法,根据配置文件的参数来建立接口 * @return 创造好的Api对象 */ public static Api createApi(){ //直接读取配置文件来获取须要建立实例的类 //至于如何读取Properties,还有如何反射这里就不解释了 Properties p = new Properties(); InputStream in = null; try { in = Factory.class.getResourceAsStream( "FactoryTest.properties"); p.load(in); } catch (IOException e) { System.out.println( "装载工厂配置文件出错了,具体的堆栈信息以下:"); e.printStackTrace(); }finally{ try { in.close(); } catch (IOException e) { e.printStackTrace(); } } //用反射去建立,那些例外处理等完善的工做这里就不作了 Api api = null; try { api = (Api)Class.forName(p.getProperty("ImplClass")) .newInstance(); } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } return api; } }
(3)此时的客户端就变得很简单了,再也不须要传入参数,代码示例以下:
public class Client { public static void main(String[] args) { Api api = Factory.createApi(); api.test1("哈哈,没关系张,只是个测试而已!"); } }
把上面的示例代码敲到电脑里面,测试一下,体会体会。
1:简单工厂的本质
简单工厂的本质是:选择实现。
注意简单工厂的重点在选择,实现是已经作好了的。就算实现再简单,也要由具体的实现类来实现,而不是在简单工厂里面来实现。简单工厂的目的在于为客户端来选择相应的实现,从而使得客户端和实现之间解耦,这样一来,具体实现发生了变化,就不用变更客户端了,这个变化会被简单工厂吸取和屏蔽掉。
实现简单工厂的难点就在于 “如何选择”实现,前面讲到了几种传递参数的方法,那都是静态的参数,还能够实现成为动态的参数。好比:在运行期间,由工厂去读取某个内存的值,或者是去读取数据库中的值,而后根据这个值来选择具体的实现等等。
2:什么时候选用简单工厂
建议在以下状况中,选用简单工厂:
简单工厂模式结束,谢谢观赏
---------------------------------------------------------------------------
私塾在线学习网原创内容 跟着cc学设计系列 之 研磨设计模式
原创内容,转载请注明出处【http://sishuok.com/forum/blogPost/list/117.html】
---------------------------------------------------------------------------