第十五章:工厂模式

一、什么是工厂模式

专门定义一个类来负责创建其他类的实例,根据参数的不同创建不同类的实例,被创建的实例通常具有共同的父类,这个模式叫简单工厂模式(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)产品等级结构稳定,设计完成之后,不会向系统中增加新的产品等级结构或者删除已有的产品等级结构。
资料:
《人人都懂设计模式:从生活中领悟设计模式》

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容