作为创建型设计模式,带有工厂名字的设计模式共有三个,分别是
- Simple Factory
- Factory Method
- Abstract Factory
其中的 Simple Factory并不是GoF一书中的模式,但是它是最基础最常用的,并且也是循序渐进的了解另外两个工厂的必要基础,所有放在一起讲它们是比较科学的。
三者常常是容易搞混的,我就见过若干个搞混的案例。要么,比起这更难的,是不太容易弄明白使用的场景和目的。本文试图通过一个案例,讲清楚三者的内涵,但是不准备讲解它的外延。
假设我们想要做一个图形编辑器,我们把它的需求压低到极为简洁的形式,只要和当前要描述的问题无关的,我们都不会引入:
- 可以创建两种形状,矩形和圆形
- 可以设置形状的颜色,红色和黄色
那么,系统中必然需要如下的Shape类:
class Shape{draw(){}}
class Rect extends Shape{draw(){}}
class Circle extends Shape{draw(){}}
以及,系统中必然需要如下的Color类:
class Color{fill(){}}
class Red extends Color{fill(){}}
class Yellow extends Color{fill(){}}
我们首先从Shape开始。假设需要创建一个矩形,我们可以这样做:
var rect = new Rect()
需要一个圆形也简单:
var rect = new Circle()
实际上,我们通常是在界面上,一般是工具栏上,放置两个按钮,让用户选择哪个按钮,然后创建此形状。用户选择了矩形,接下来创建就是矩形,选择的是圆形,那么创建就是圆形。所以这样的代码一定是存在的:
if (userSelected = "rect")
return new Rect()
if (userSelected = "circle")
return new Circle()
Simple Factory
Simple Factory的价值就是让调用者从创建逻辑中解脱,只要传递一个参数,就可以获得创建对象。实际上,从对象职责来说,这段代码不应该是Rect或者是Circle的,也不应该是UI类的,UI类在不同的应用中是不一样的,但是我们知道作为顶层类,需要负责UI显示和事件,不应该负责创建对象的逻辑。实际上,很多代码放到此处,特别容易导致代码拥挤,主控类职责过多的问题。
最好引入一个新的类,像是这样:
class ShapeCREATEOR{
create(userSelected){
if (userSelected = "rect")
return new Rect()
if (userSelected = "circle")
return new Circle()
}
}
这个类的所有逻辑,都是专门用于创建其他类。因为非常常见,人们为他取名为Factory,其他被创建的类被称为Product。所以惯例上来说,此类的名字会冠以工厂名:
class ShapeFactory
根据传入的参数,决定创建哪一个产品类,此类就被称为简单工厂类(Simple Factory)。有了工厂类,使用者就可以直接使用工厂类获得需要的对象:
var sf = new ShapeFactory()
var rect = sf.create("rect")
于是,所有需要创建矩形的场合,你知道,一个UI App,除了工具栏,还有菜单,都只要写这样的代码就可以创建了。而不必到处根据userSelected来做分支了。这就是使用工厂的好处。如果支持命令创建,甚至使用json文件中恢复对象时,本来也需要传递字符串来决定创建对象时,就显得简单工厂的好处了。
factory method
简单工厂根据传入的参数决定实例化哪一个类,而factory method有子类来决定实例化哪一个类。
class Shape{draw(){}}
class Rect extends Shape{draw(){}}
class Circle extends Shape{draw(){}}
class ShapeFactory{
createShape(){}
}
class RectFactory extends ShapeFactory{
createShape(){return new Rect()}
}
class CircleFactory extends ShapeFactory{
createShape(){return new Circle()}
}
调用者需要创建Rect,只要这样:
var f = new RectFactory()
f.createShape()
这是factory method的定义:
创建一个接口,但是由子类决定实例化哪一个类
这里提到的接口是ShapeFactory.createShape,提到的子类为:RectFactory,CircleFactory。这样做就意味着,在工厂内不必根据传入参数分支,它作为子类本身就知道要创建的是哪一个产品。使用对应的工厂,创建需要的类。
AbstractFactory
要是我们创建的类型不仅仅是Shape,还有Color的话,AbstractFactory就有价值。AbstractFactory提供一个接口a,此接口可以创建一系列相关或者相互依赖的对象b,使用用户不需要指定具体的类即可创建它们c。
我们先看代码:
class Shape{draw(){}}
class Rect extends Shape{draw(){}}
class Circle extends Shape{draw(){}}
class ShapeFactory{
createShape(type){
if (shape == "rect"){
return new Rect()
}else{
return new Circle()
}
}
}
class Color{fill(){}}
class Red extends Color{fill(){}}
class Yellow extends Color{fill(){}}
class ColorFactory {
creatColor(type){
if (shape == "Red"){
return new Red()
}else if (shape == "Yellow"{
return new Yellow()
}
}
}
如果希望客户可以一个单一接口来访问Color和Shape,可以引入一个抽象工厂:
class AbstractFactory{
createShape(){}
createColor(){}
}
要求两个工厂实现此抽象工厂:
class ShapeFactory extends AbstractFactory{
createShape(type){
if (shape == "rect"){
return new Rect()
}else{
return new Circle()
}
}
createColor(){
return null
}
}
class ColorFactory extends AbstractFactory{
createShape(type){return null}
creatColor(type){
if (shape == "Red"){
return new Red()
}else if (shape == "Yellow"{
return new Yellow()
}
}
}
自己不具备的能力,不实现即可,这里就是返回一个null。需要一个创建工程的简单工厂
class FactoryProducer{
getFactory(type){
if (type == "color")return new ColorFactory()
else return new ShapeFactory()
}
}
没有抽象工厂,那么代码是这样的,所有的Factory类的创建都是硬编码的
var sf = new ShapeFactory()
var r = sf.createColor("Rect")
r.draw()
var cf = new ColorFactory()
var c = cf.createColor("Red")
c.fill()
有了抽象工厂,那么客户的使用就是这样
var fp = new FactoryProducer()
var sf = fp.getFactory("shape")
var r = sf.createColor("Rect")
r.draw()
var cf = fp.getFactory("color")
var c = cf.createColor("Red")
c.fill()
好处是,硬编码创建的类只有一个,就是FactoryProducer。
其中难懂的部分,做一个进一步说明:
- 接口a:AbstractFactory内的两个函数createShape,createColor
- 一系列相关或者相互依赖的对象b: Shape系列类,Color系列类
- 使用用户不需要指定具体的类即可创建它们c:实际上,用户只要使用FactoryProducer这一个类,不需要使用任何一个工厂,以及工厂创建的类。
本文host于 https://github.com/1000copy/d... ,欢迎folk。