资讯专栏INFORMATION COLUMN

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

diabloneo / 2260人阅读

摘要:假设需要创建一个矩形,我们可以这样做需要一个圆形也简单实际上,我们通常是在界面上,一般是工具栏上,放置两个按钮,让用户选择哪个按钮,然后创建此形状。

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

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。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/92086.html

相关文章

  • Spring IOC知识点一网打尽

    摘要:使用的好处知乎的回答不用自己组装,拿来就用。统一配置,便于修改。 前言 只有光头才能变强 回顾前面: 给女朋友讲解什么是代理模式 包装模式就是这么简单啦 单例模式你会几种写法? 工厂模式理解了没有? 在刷Spring书籍的时候花了点时间去学习了单例模式和工厂模式,总的来说还是非常值得的! 本来想的是刷完《Spring 实战 (第4版)》和《精通Spring4.x 企业应用开发实战》...

    djfml 评论0 收藏0
  • 腾讯云服务器2020年6月促销活动整理,腾讯云最便宜的云服务器一网打尽

    摘要:文章目录一云聚惠一云聚惠二星星海云服务器二星星海云服务器三企业用户福利专场三企业用户福利专场腾讯云怎么样腾讯云是腾讯公司旗下的云计算服务品牌,国内知名企业,云服务器产品自然稳定靠谱,是个人企业的最佳选择,适合建站小程序项目游戏服务 文章目录一、618云聚惠二、星星海云服务器三、企业用户福利专场腾讯云怎么样?腾讯云是腾讯公司旗下的云计算服务品牌,国内知名IT企业,云服务器产品自然稳定...

    warnerwu 评论0 收藏0
  • 我的Java设计模式-单例模式

    摘要:那有什么办法保证只有一个领导人斯大林呢较常见的两种方式饿汉式和懒汉式二实战图这里提示一点,在学习设计模式的时候,图会让你更容易,而且深刻的去理解到该模式的核心。下一篇的设计模式是工厂方法模式。   就算不懂设计模式的兄弟姐妹们,想必也听说过单例模式,并且在项目中也会用上。但是,真正理解和熟悉单例模式的人有几个呢?接下来我们一起来学习设计模式中最简单的模式之一——单例模式 一、为什么叫单...

    Jensen 评论0 收藏0

发表评论

0条评论

diabloneo

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<