第十五章:工厂模式

一、什么是工厂模式

专门定义一个类来负责创建其他类的实例,根据参数的不同创建不同类的实例,被创建的实例通常具有共同的父类,这个模式叫简单工厂模式(Simple Factory Pattern)
简单工厂模式又称为静态工厂方法模式。之所以叫“静态”,是因为在很多静态语言(如Java、C++)中方法通常被定义成一个静态(static)方法,这样便可通过类名来直接调用方法。

1.1根据剧情来体会

生活中咖啡机的使用也非常简单,咖啡机旁边有已经准备好的咖啡豆,想喝咖啡,只要往咖啡机里加入少量的咖啡豆,然后选择杯数和浓度,再按一下开关,10分钟后,带着浓香的咖啡就为你准备好了!当然,如果你想喝一些其他口味的咖啡,也可以自备咖啡豆,无论你要拿铁还是摩卡,都不是问题。那么问题来了,你要拿铁还是摩卡呢?
用python代码来演绎:

from abc import ABCMeta, abstractmethod


# 引入ABCMeta和abstractmethod来定义抽象类和抽象方法
class Coffee(metaclass=ABCMeta):
    def __init__(self, name):
        self.name = name

    def getName(self):
        return self.name

    @abstractmethod
    def getTaste(self):
        pass


class LatteCaffe(Coffee):
    """拿铁咖啡"""

    def __init__(self, name):
        super().__init__(name)

    def getTaste(self):
        return "轻柔而香醇"


class MochaCoffee(Coffee):
    """摩卡咖啡"""

    def __init_(self, name):
        super().__init__(name)

    def getTaste(self):
        return "丝滑与醇厚"


class Coffeemaker:
    """咖啡机"""

    @staticmethod
    def makeCoffee(coffeeBean):
        """通过staticmethod装饰器修饰来定义一个静态方法。"""
        if coffeeBean == "拿铁咖啡豆":
            coffee = LatteCaffe("拿铁咖啡")
        elif coffeeBean == "摩卡咖啡豆":
            coffee = MochaCoffee("摩卡咖啡")
        else:
            raise ValueError("不支持的参数:%s", coffeeBean)
        return coffee


def testCoffeeMaker():
    latte = Coffeemaker.makeCoffee("拿铁咖啡豆")
    print("%s己为您准各好了,口感:%s。请慢慢享用!" % (latte.getName(), latte.getTaste()))
    mocha = Coffeemaker.makeCoffee("摩卡咖啡豆")
    print("%s己为您准备好了,口感:%s。请慢慢享用!" % (mocha.getName(), mocha.getTaste()))


if __name__ == "__main__":
    testCoffeeMaker()

用golang代码来演示:

package main

import (
    "errors"
    "fmt"
)

type abcCoffee interface {
    GetName() string
    GetTask() string
}

// Coffee 咖啡的抽象类
type Coffee struct {
    Name string
}

func (c Coffee) GetName() string {
    return c.Name
}

func (c Coffee) GetTask() string {
    return ""
}

// LatteCoffee 拿铁咖啡
type LatteCoffee struct {
    Coffee
}

func (l LatteCoffee) GetTask() string {
    return "轻柔而香醇"
}

// MochaCoffee 摩卡咖啡
type MochaCoffee struct {
    Coffee
}

func (m MochaCoffee) GetTask() string {
    return "丝滑与醇厚"
}

// CoffeeMaker 咖啡机
type CoffeeMaker struct {
}

func (c CoffeeMaker) makeCoffee(coffeeBean string) (abcCoffee, error) {
    if coffeeBean == "拿铁咖啡豆" {
        coffee := LatteCoffee{Coffee{coffeeBean}}
        return coffee, nil
    }
    if coffeeBean == "摩卡咖啡豆" {
        coffee := MochaCoffee{Coffee{coffeeBean}}
        return coffee, nil
    }
    return nil, errors.New(fmt.Sprintf("不支持的参数:%s", coffeeBean))
}

func main() {
    cm := CoffeeMaker{}
    if latte, err := cm.makeCoffee("拿铁咖啡豆"); err != nil {
        fmt.Println(err)
    } else {
        fmt.Printf("%s 已经为您准备好了,口感:%s。请慢慢享用!\n", latte.GetName(), latte.GetTask())
    }
    if mocha, err := cm.makeCoffee("摩卡咖啡豆"); err != nil {
        fmt.Println(err)
    } else {
        fmt.Printf("%s 已经为您准备好了,口感:%s。请慢慢享用!\n", mocha.GetName(), mocha.GetTask())
    }
}

运行结果:

拿铁咖啡豆 已经为您准备好了,口感:轻柔而香醇。请慢慢享用!
摩卡咖啡豆 已经为您准备好了,口感:丝滑与醇厚。请慢慢享用!

1.2定义

简单工厂模式
定义一个创建对象(实例化对象)的接口,通过参数来决定创建哪个类的实例。

1.3 优缺点

优点:
(1)实现简单、结构清晰。
(2)抽象出一个专门的类来负责某类对象的创建,分割出创建的职责,不能直接创建具体的对象,只需传入适当的参数即可。
(3)使用者可以不关注具体对象的类名称,只需知道传入什么参数可以创建哪些需要的对象。
缺点:
(1)不易拓展,一旦添加新的产品类型,就不得不修改工厂的创建逻辑。不符合“开放封闭”原则,如果要增加或删除一个产品类型,就要修改switch...case...(或if...else...)的判断代码。
(2)当产品类型较多时,工厂的创建逻辑可能过于复杂,switch...case...(或if...else...)判断会变得非常多。一旦出错可能造成所有产品创建失败,不利于系统的维护。

1.4 应用场景

(1)产品具有明显的继承关系,且产品的类型不太多。
(2)所有的产品具有相同的方法和类似的属性,使用者不关心具体的类型,只希望传入合适的参数能返回合适的对象。
尽管简单工厂模式不符合“开放-封闭”原则,但因为它简单,所以仍然能在很多项目中看到它。
二、工厂方法模式
工厂方法模式是简单工厂模式的一个升级版本,为解决简单工厂模式不符合“开放-封闭”原则的问题,我们对 SimpleFactory 进行了一个拆分,抽象出一个父类 Factory,并增加多个子类分别负责创建不同的具体产品。

2.1 定义

定义一个创建对象(实例化对象)的接口,让子类来决定创建哪个类的实例。工厂方法使一个类的实例化延迟到其子类。

2.2 代码实例:

package main

import (
    "errors"
    "fmt"
)

type Coffee interface {
    getName() string
    getTask() string
}

type AbsCoffee struct {
    Name string
}

func (c AbsCoffee) getName() string {
    return c.Name
}

func (c AbsCoffee) getTask() string {
    return ""
}

type LatteCoffee struct {
    AbsCoffee
}

func (c LatteCoffee) getTask() string {
    return "轻柔而香醇"
}

type MochaCoffee struct {
    AbsCoffee
}

func (c MochaCoffee) getTask() string {
    return "丝滑与醇厚"
}

type AbsCoffeeMaker struct {
}

func (m AbsCoffeeMaker) makeCoffee() (Coffee, error) {
    return AbsCoffee{}, errors.New("空的咖啡杯")
}

type LatteCoffeeMaker struct {
    AbsCoffeeMaker
}

func (c LatteCoffeeMaker) makeCoffee() (Coffee, error) {
    return LatteCoffee{AbsCoffee{"拿铁咖啡"}}, nil
}

type MochaCoffeeMaker struct {
    AbsCoffeeMaker
}

func (c MochaCoffeeMaker) makeCoffee() (Coffee, error) {
    return MochaCoffee{AbsCoffee{"摩卡咖啡"}}, nil
}

func main() {
    lcm := LatteCoffeeMaker{}
    if latte, err := lcm.makeCoffee(); err != nil {
        fmt.Println(err)
    } else {
        fmt.Printf("%s 已经为您准备好了,口感:%s。请慢慢享用!\n", latte.getName(), latte.getTask())
    }
    mcm := MochaCoffeeMaker{}
    if mocha, err := mcm.makeCoffee(); err != nil {
        fmt.Println(err)
    } else {
        fmt.Printf("%s 已经为您准备好了,口感:%s。请慢慢享用!\n", mocha.getName(), mocha.getTask())
    }
}

2.3 优缺点

“优点:
(1)解决了简单工厂模式不符合“开放-封闭”原则的问题,使程序更容易拓展。
(2)实现简单。
缺点:
对于有多种分类的产品,或具有二级分类的产品,工厂方法模式并不适用。
多种分类:如我们有一个电子白板程序,可以绘制各种图形,那么画笔的绘制功能可以理解为一个工厂,而图形可以理解为一种产品;图形可以根据形状分为直线、矩形、椭圆等,也可以根据颜色分为红色图形、绿色图形、蓝色图形等。
二级分类:如一个家电工厂,它可能同时生产冰箱、空调和洗衣机,那么冰箱、空调、洗衣机属于一级分类;而洗衣机又可分为高效型的和节能型的,高效型洗衣机和节能型洗衣机就属于二级分类。

2.4.应用场景

(1)客户端不知道它所需要的对象的类。
(2)工厂类希望通过其子类来决定创建哪个具体类的对象。
三、抽象工厂模式
抽象工厂模式是工厂方法模式的升级版本,工厂方法模式不能解决具有二级分类的产品的创建问题,抽象工厂模式就是用来解决这一问题的。

3.1 定义

提供一个创建一系列相关或相互依赖的对象的接口,而无须指定它们的具体类。

3.2代码实现

package main

import "fmt"

type HomeAppliances interface {
    getName() string
}

// Refrigerator 冰箱
type Refrigerator struct {
    name string
}

func (r Refrigerator) getName() string {
    return "冰箱"
}

// EfficientRefrigerator 高效冰箱
type EfficientRefrigerator struct {
    Refrigerator
}

func (e EfficientRefrigerator) getName() string {
    return e.name
}

// EnergySavingRefrigerator 节能冰箱
type EnergySavingRefrigerator struct {
    Refrigerator
}

func (e EnergySavingRefrigerator) getName() string {
    return e.name
}

// WashingMachine 洗衣机
type WashingMachine struct {
    name string
}

func (w WashingMachine) getName() string {
    return "洗衣机"
}

// EfficientWashingMachine 高效洗衣机
type EfficientWashingMachine struct {
    WashingMachine
}

func (e EfficientWashingMachine) getName() string {
    return e.name
}

// EnergySavingWashingMachine 节能洗衣机
type EnergySavingWashingMachine struct {
    WashingMachine
}

func (e EnergySavingWashingMachine) getName() string {
    return e.name
}

type AppliancesFactory interface {
    CreateRefrigerator() HomeAppliances
    CreateWashingMachine() HomeAppliances
}

type EfficientFactory struct {
}

func (e EfficientFactory) CreateRefrigerator() HomeAppliances {
    return &EfficientRefrigerator{Refrigerator{
        name: "高效冰箱",
    }}
}

func (e EfficientFactory) CreateWashingMachine() HomeAppliances {
    return &EfficientWashingMachine{WashingMachine{
        name: "高效洗衣机",
    }}
}

type EnergySavingFactory struct {
}

func (e EnergySavingFactory) CreateRefrigerator() HomeAppliances {
    return &EnergySavingRefrigerator{Refrigerator{
        name: "节能冰箱",
    }}
}

func (e EnergySavingFactory) CreateWashingMachine() HomeAppliances {
    return &EnergySavingWashingMachine{WashingMachine{
        name: "节能洗衣机",
    }}
}

func main() {
    efficientFactory := new(EfficientFactory)
    energySavingFactory := new(EnergySavingFactory)

    efficRefrigerator := efficientFactory.CreateRefrigerator()
    efficWashingMachine := efficientFactory.CreateWashingMachine()

    engryRefrigerator := energySavingFactory.CreateRefrigerator()
    engryWashingMachine := energySavingFactory.CreateWashingMachine()

    fmt.Println(efficRefrigerator.getName())
    fmt.Println(efficWashingMachine.getName())
    fmt.Println(engryRefrigerator.getName())
    fmt.Println(engryWashingMachine.getName())
}

AppliancesFactory是一个抽象的工厂类,定义了三个方法,分别用来生产冰箱(Refrigerator)、空调(Air-conditioner)、洗衣机(WashingMachine)。EfficientFactory和EnergySavingFactory是两个具体的工厂类,分别用来生产高效型的家电和节能型的家电。

3.3 优缺点

优点:
解决了具有二级分类的产品的创建。
缺点:
(1)如果产品的分类超过二级,如三级甚至更多级,抽象工厂模式将会变得非常臃肿。
(2)不能解决产品有多种分类、多种组合的问题。

3.4应用场景

(1)系统中有多于一个的产品族,而每次只使用其中某一产品族。
(2)产品等级结构稳定,设计完成之后,不会向系统中增加新的产品等级结构或者删除已有的产品等级结构。
资料:
《人人都懂设计模式:从生活中领悟设计模式》

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,098评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,213评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,960评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,519评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,512评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,533评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,914评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,574评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,804评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,563评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,644评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,350评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,933评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,908评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,146评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,847评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,361评论 2 342

推荐阅读更多精彩内容