设计模式:工厂三姐妹一网打尽

作为创建型设计模式,带有工厂名字的设计模式共有三个,分别是

  1. Simple Factory
  2. Factory Method
  3. Abstract Factory

其中的 Simple Factory并不是GoF一书中的模式,但是它是最基础最常用的,并且也是循序渐进的了解另外两个工厂的必要基础,所有放在一起讲它们是比较科学的。

三者常常是容易搞混的,我就见过若干个搞混的案例。要么,比起这更难的,是不太容易弄明白使用的场景和目的。本文试图通过一个案例,讲清楚三者的内涵,但是不准备讲解它的外延。

假设我们想要做一个图形编辑器,我们把它的需求压低到极为简洁的形式,只要和当前要描述的问题无关的,我们都不会引入:

  1. 可以创建两种形状,矩形和圆形
  2. 可以设置形状的颜色,红色和黄色

那么,系统中必然需要如下的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。

其中难懂的部分,做一个进一步说明:

  1. 接口a:AbstractFactory内的两个函数createShape,createColor
  2. 一系列相关或者相互依赖的对象b: Shape系列类,Color系列类
  3. 使用用户不需要指定具体的类即可创建它们c:实际上,用户只要使用FactoryProducer这一个类,不需要使用任何一个工厂,以及工厂创建的类。

本文host于 https://github.com/1000copy/d... ,欢迎folk。

你可能感兴趣的:(design-pattern,javascript)