一、工厂模式定义:
根据给出的信息创建特定类的实例,以达到程序解耦的目的。
二、使用场景目的:
一般用在创建对象的情况比较复杂,在整个程序的开发及运维阶段,无法预知将要
创建哪些类的实例。将直接创建对象的方式改为从工厂中获取,达到解耦的目的。
举个例子:在人类文明尚且处于蒙昧的阶段,我们需要某种水果时,唯一的办法也
许就是自己去摘取,需要其它某些食物时,也同样如此,这样严重阻碍了生产力的
发展。随着社会的进步,出现了交换行为,进而出现了社会分工。有专门负责种植
水果的人,专门负责贩卖水果的人(单一职责原则)。这样尚嫌不够,我们想吃葡
萄的时候,不一定非要去找专门贩卖葡萄的商贩,通常一个水果商会贩卖多种水
果,我们仅需要告诉他我们想要什么类型的水果,然后由他向我们提供即可,至于
每一种水果是怎么来的,则与我们无关。我们不需要关心水果是产自哪棵果树、以
及这颗果树是怎么管理的。这就是工厂模式的思想。这是政治经济学的一个例子,
不属于本章的范畴,但它很能说明编程中的问题。如果想要更深入的了解,请参看
亚当斯密的《国富论》第一卷:论分工。之所以举这个例子是为了说明:政治经济
学的方法论是哲学的,计算机科学的方法论也同样是哲学的。我们不应该执着于具
体的实现,而应该具备某些思想,这些思想是解决问题的源泉。
三、工厂模式分类:
1、简单工厂模式:
简单工厂模式实现起来尤为简单,以至于很多人不把它归纳到工厂模式中来。但
它确是工厂模式的一种。例如几何学中对于钝角的定义如下:“大于直角的角叫做
钝角”,那么只要符合这个定义的我们都可以称之为钝角。简单工厂模式也一样,
不能因为它过于简单,而排除在工厂模式之外。同样的,我们也可以使用其它方
法实现工厂模式,只要它能够达到解耦的目的,我认为这些都是可行的。我会牢
记一个原则:能够最简单、最直接的达到目的的时候,我不会去走任何曲折的道
路。
代码:
该接口提供了一个抽象的水果品类,用于实现多态。
以上三个具体的苹果、香蕉、桃子实现了水果接口,也就是说,它们都是水果,具有同一个类型。
在水果工厂中使用了一个枚举类,用于定义水果的类型。还有一个购买水果的方法,传入相应的水果类型信息,获取特定水果的实例。
至此一个简单工厂模式就实现了,我认为情况不是特别复杂的情况下,使用这种方式就很大程度上做到了代码的解耦。当然这种方式也是有它的缺点的:如果我们要水果工厂再多生产几种水果,那么不可避免的需要添加枚举类型、需要在购买水果方法中多几个case语句。这样的做法显然违背了开闭原则,也不够优雅。但是使用反射的方式呢?
下面我们改造一下FruitFactory类:
现在工厂类只存在一个购买水果的方法,并且它的入参发生了变化:它不再需要一个水果的枚举类型,而是需要传入一个实现了水果接口的任意类型的class对象。现在这个工厂已经具备了一种动态扩展的性质,如果我们需要添加新的水果,仅仅让这个新的水果类实现水果接口即可,无须增加或修改任何代码,已经可以从工厂类中获取任意水果类型的实例了。我个人认为这是简单工厂模式完美的实现,虽然这个工厂类仅仅使用了几行代码。但是我们想想之前的那个原则(如果认同的话):能够使用最直接、最简单的方式达到目的,绝不采用任何曲折繁杂的方式去实现。当然,这种实现方式也并非无可挑剔,比如:如果构造实例时需要初始化参数、构造器私有等等。但这是工厂模式都能够遇到的问题,使用这种方式也是可以进行解决的。spring中就大量使用了反射来创建对象,我认为这种方式之所以没有被人提及,不过是因为过于简单罢了。
2、工厂方法模式
定义一个水果工厂接口,里面仅有购买水果这一抽象行为,至于买什么水果,要在具体工厂里面去实现。
新建两个具体的水果工厂类,都实现水果工厂接口。
这种模式解决了需要创建对象种类非常多、且创建对象方式复杂的情况下,频繁修改购买水果接口,增加购买水果方法的复杂度。很大程度上遵守了开闭原则。但是很显然,这种方式不过是把方法的复杂度转移到了类级别上,如果项目复杂程度较高时,这种方式无疑是一种比较好的选择。这种方式的缺点也是很明显的:针对每一个具体产品创建一个工厂类,成本是不是太高了?
3、抽象工厂模式:
接下来我们想象会遇到这样一种场景:我们购买各种水果来制作不同的水果蛋糕。现
在仅仅是购买水果还是不够的,我们还需要购买奶油及其它材料,现在修改一下代
码:
定义一个抽象接口,并提供一个获取奶油种类名称的方法。
定义一个动物淡奶油类,然后蛋糕制作商为了节约成本,还会选择人造奶油作为原料。现在这两种材料都实现了奶油接口,因此是无差别的。这两种材料制作的蛋糕都叫做奶油蛋糕。
定义一个抽象工厂类。
一个具体的水果工厂,继承了抽象工厂,并提供根据传入的class对象,动态获取水果的方法。
具体的奶油工厂,实现了获取奶油的抽象方法。
创建一个工厂建造者类,根据传入的class对象,动态获取工厂实例。获取工厂实例的方法仍然使用反射动态获取。
测试类中,根据特定工厂类的class对象,获取相应的工厂对象,并且根据具体的水果及奶油类型,获取到了具体的水果,最后制作成了廉价的“苹果人造奶油蛋糕”。
结论:
抽象工厂模式用于解决多个不同的类实例的创建,其可扩展性要优于简单工厂模式
及工厂方法模式。但就其实现方式来说,也比其他工厂模式要更加复杂。而且需要
针对每一个类创建一个工厂实现类,并在抽象工厂中加入获取具体工厂的方法。工
厂模式充分利用了java多态的特性,是利用多态进行解耦的一个比较好的实现,也
是依赖倒置原则的一种体现。但在使用时应该根据具体情况来判断使用哪一种模式
更好,毕竟不能像本章的demo一样,为了“输出一句hello word”而实现一个抽象工
厂模式。本章的demo仅仅是一个例子,用来说明工厂模式的具体实现。