创建型模型
创建型模型抽象了实例化过程,它们帮助一个系统独立于如何创建、组合和表示它的那些对象。类创建型模型使用继承改变实例化的类,而对象创建模型将实例化委托给另一个对象
在这些模式中有两个不断出现的主旋律。第一,他们都将关于该系统使用哪些具体的类的信息封装起来。第二,他们隐藏了这些类的示例是如何被创建和放在一起的。整个系统关于这些对象所知道的是有抽象类所定义的接口。因为,创建型模式在什么地方被创建,又谁创建,它是怎么被创建的,以及何时被创建这几个方面给予很大的灵活性。它们允许你用结构和功能差别很大的“产品”对象配置一个系统。配置可以是静态的(即编译时制定)。也可以是动态的(在运行时)
目前创建型模型主要有
- 抽象工厂(ABSTRACT FACTORY)
- 生成器(BUILDER)
- 工厂方法(FACTORY METHOD)
- 原型(PROTOTYPE)
- 单件(SINGLETON)
本篇主要介绍工厂方法和抽象工厂,以下通过demo演示从
没有模式->简单工厂->工厂方法->抽象工厂 的过程
简单工厂
假设你是一个农夫,你开垦了一块土地并准备大展身手,你打算先随便种点东西试一试土地合不合格,于是播撒下不知名野种,不久后长出了各式各样野果,这个过程中没有用到任何模式,落实到代码中就是1
2
3
4YG yeguo = new YG();//野果
yeguo.bozhong();//播种
yeguo.jiaoshui();//浇水
yeguo.shouhuo();//收获
检验过土地之后,你准备开始正式种地了,目前的情况是你只有这一小块地,他一次只能种一类种子;种子可以买,但是并不是所有种子都是一个季度种植的。所以你打算根据季度来种下不同的种子,获得不同的产物(这里不考虑收获周期)。我们假设你打算春季种植大葱,秋季种植油菜,UML图如下
这时候种植代码大致如下所示
1 | Product product ;//农作物 |
我们假设把实例化农作物那部分代码封装到一个类Factory
里面,这个类返回一个Product
实例,那么即是简单工厂
这时候Client代码如下所示
1 | Pruduct product = (new Factory()).CreatePruduct(); |
在简单工厂中,我们将大葱和油菜抽象为农作物,它们共同继承了农作物,实现了农作物的三个接口,它们共同组成了产品部分;然后将实例化部分代码封装进工厂类中,这样你就不无需关注产品是如何实例化的,也不知道实例化对象具体是哪个产品,反正你只需要操作他的接口方法就可以了
在简单工厂中,假设你需要新增一个农产品–萝卜,你需要新增一个萝卜类,并让它实现农产品接口的几个接口方法,这看起来没什么难的。但是!你还要修改工厂方法中的实例化过程(示例中的switch case
那段代码),不然它永远不会帮你实例化一个萝卜出来,这样的话工厂方法中就违反了一个设计模式中的基本原则–开放封闭原则,即对修改封闭,对扩展开放,我们可以新增类,但不能轻易修改已经存在的类。这个问题也有一些取巧的方法处理,利用反射来实例化不同产品。综上,简单工厂严格意义上来说并不算一个合格的设计模式,它只不过利用产品接口来统一产品类别,再将实例化方法封装到一个单独的类别中,并没有什么扩展性可言,在不修改旧代码的情况下可以说无法扩展
简单工厂的优缺点
- 通过接口调用不同实例的方法,符合面向接口编程的基本思路,减轻了客户端和具体产品类的耦合
- 将实例化的过程封装到一个具体工厂类中,客户无需关注自己实例化过程,进一步减轻了客户的负担,但是如果增加新的产品,这个具体工厂类就必须修改自己的实例化过程代码,不符合开放封闭原则
- 通过接口调用的缺点:产品接口中的方法一旦确定就无法改变,因为一旦改变就要修改其所有子类
工厂类中反射
反射大致思路有几种
- 构造工厂类是传入类名,以此反射
- 工厂类内部根据某个配置项或某个变量值,以此反射
工厂方法
在简单工厂模式下,你在一块土地上种植了大葱、油菜、花生、玉米等等农作物,工厂类总是能帮你实例出各种应季农作物,你只要做好自己的工作(播种,浇水,收获),如此就够了。随着时间的流逝,你膨胀了,你觉得自己应该扩张一块地,用来种植一些经济作物,比如水果、烟草。但是简单工厂模式下无法满足这个扩展,因为工厂类只有一个,那么我们尝试把工厂类抽象化,再把农作物和经济作物抽象为作物,UML图如下
图中的土地和工厂统一为一个类,不做区分。这时候Client不单单是简单的调用工厂的CreatePruduct
获取实例了,因为首先需要先选择一个工厂子类(土地A或土地B),这种情况下如果在新增一个作物类型,也只需要新增一个工厂类和对应的作物类就可以,这个时候你的注意力比以前高了一个维度,之前你关注土地种出的具体作物是萝卜还是油菜,现在只需要关注它是农作物还是经济作物就可以,至于具体农作物种植什么,可以在农作物中通过简单工厂方法实现
抽象工厂
随着时光的流逝,你的工厂和土地已经扩充到了960万平方公里,地球上能中的东西都被你种了一个遍,收获的作物可以到地球三十圈,这时候你突然发现了转基因这个概念,你将继续扩充土地,支持转基因作物
这时UML图变成下图
这时你可以在转基因和非转基因这个增面进行扩张,而作物类别已经无法进行扩张了,因为你个抽象工厂已经确定了只能生产农作物和经济作物
适用性
- 一个系统要独立于它的产品的创建、组合和表示时
- 一个系统要由多个产品系列中的一个来配置时
- 当你要强调一系列相关的产品对象的设计以便进行联合使用时
- 当你提供你个产品类库,而只想显示它们的接口而不是实现时
效果
- 他分离了具体的类 因为一个工厂封装创建产品对象的责任和过程,它将客户和类的实现分离。客户通过它们的抽象接口操纵实例。产品的类名也在具体工厂的实现中被分离;它们不出现在客户代码中
- 它使得易于交换产品系列 一个具体工厂类在一个应用仅出现以此–即在它初始化的时候。这使得改变一个应用的具体工厂变得容易。它只需改变具体的工厂既可以使用不同的产品配置,这是因为一个抽象工厂创建了一个完整的产品系列,所以整个产品系列会立刻改变
- 他有利于产品的一致性 当一个系列中的产品对象被设计成一起工作时,一个应用一次只能使用同一个系列中的对象
- 难以支持新品种的产品 难以扩展抽象工厂以生产新种类的产品。这是因为抽象工厂接口确定了可以被创建的产品集合。支持心种类的产品就需要扩展该工厂接口,这将涉及所有子类的改变
参照
- 《设计模式-可复用面向对象软件的基础》