分享

怎么样让你写的程序分工又分家?

 网罗灯下黑 2020-11-12

导语:这次是开发者蓝图系列的第二篇,让我们好好聊聊工厂方法模式的前世今生。

之前哥就说了,设计模式是源于万千程序猿的真实项目的开发经验,并超脱这些经验,给出的解决方案。

工厂方法模式当然也不例外,它和之前讲过的单例模式一样,也是为了解决创建对象时所出现的问题,有兴趣的小伙伴不妨去去瞅瞅之前那篇单例模式,谁让它是面试题的常客呢。

听说你的程序还不会计划生育?(单例模式理论篇)

什么是工厂方法模式

工厂方法模式又叫做虚拟构造函数,属于创建型设计模式,不知道设计模式分类的小伙伴快去看看猿哥下面这篇文章。

程序猿必备系列:开发者的蓝图之初探分类

那话说回来,到底什么是工厂方法模式呢,它的正经定义是用一个创建产品对象的工厂接口,将产品对象的实际创建工作推迟到具体子工厂类当中。

猿哥就问一句,懵不懵!什么产品,什么工厂,这都哪跟哪啊!

没错,上面就是我第一次看到这个定义的赶脚,是真滴看不懂。

但别着急,猿哥给你们说一下工厂方法模式的形象版描述。

在工厂方法模式中,创建对象的类就如同一个加工创造实例对象的工厂,而这些实例对象就是被生产出来的一个个产品。

如果创建的产品不多,只要一个工厂类就可以完成生产,那这种模式就变成了“简单工厂模式”。

但它不属于 GoF 提出的 23 种经典设计模式,这里也就不具体展开了,后面谈代码实现的时候还得说它。

当所生产的产品种类繁多时,一个工厂类当然满足不了需求了,但完全创建一个新的厂子,重复性劳动又太多。

所以在简单工厂模式的基础上进一步抽象和推广,并利用继承和多态的特性,由父工厂类所提供的创建方法和接口,由子工厂类去决定具体生产哪种产品。

一个工厂类对应一个产品类,这就是我们今天要聊的工厂方法模式。


工厂方法模式解决的问题

都0202年了,谁还能否认社会分工的细化所带来工作效率的提升,在开发中,让代码们“各司其职”,一直也都是我们追求的目标。

而工厂方法模式所解决的问题,在我看来就一条,就是将产品对象的生产和使用分离,让产品对象的增删改变的更方便!

使用产品的人是不会在意产品是如何产生的,这样将生产和使用分离,不仅避免了直接实例化导致的高耦合,更摆脱了产品增删改带来的工厂逻辑的冗杂。

猿哥我知道,说这么多干巴巴的知识,很容易乏味,如果实在看不下去的小伙伴一定要瞅瞅最后的代码实现,有个印象比什么都强~

工厂方法模式的适用场景

无论是日常开发还是框架源码,工厂方法模式真的随处可见,所以它往往被我们忽视。

按工厂方法模式的特点和所解决的问题,猿哥将工厂方法模式的适用场景分为了三类。

扩展能力哪家强

猿哥觉得只要是写代码用过别家写的库或者框架的,没谁不熟悉工厂方法模式,只是灯下黑罢了。

用别人造的轮子时,是不是都需要先创建一个实例化对象,再对这个实例化对象进行自己的需求设计。

其实创建这个实例化对象的工厂类,往往还有一个功能不那么具体的父类,它才是原轮子中的标准组件,没错,继承可能是拓展轮子最简单的方法。

看过源码的小伙伴应该知道,把框架中各个组件的构造代码专门集中到一个父工厂类,定义了一系列的接口规则,而子类工厂不仅继承了这个父类,更允许使用者进行重写。

你看,当你想扩展轮子时,不需要你抽丝剥茧的去一步步寻找哪里有耦合,直接继承重写父工厂类它不香嘛。

甚至对于一些使用者而言,无需关注它背后的实现逻辑,只要知道子类工厂的名字就行,这就是我们接下来要聊的下一个适用场景。


关系未知不犯错

工厂方法模式所做到的将生产和业务代码分离,可以在不影响其他代码的前提下扩展新的产品,为你带来的便利可想而知。

如果说上面那种场景是针对轮子制造者而言,那么这个场景就是针对使用者而言。

无需知道它们背后的逻辑,无需知道具体父工厂类名,只需要了解对应的工厂名即可。


资源节省它可以

这一点和之前讲过的单例模式有点像,对于像数据库连接这样的资源密集型对象,都起到了节省内存资源的作用。

但和单例模式不同的是,有时我们既需要能够复用现有对象,又需要创建新对象,而且这些对象还需要共用一个接口。

仔细想想这不是就是工厂方法模式嘛,无非是在工厂类中加一个逻辑判断的事。

如果说上面说的还不够具体,那猿哥再给你两个具体场景:

  1. 在设计数据库访问时,当你也不知道用户会采用哪一类数据库,以及数据库可能有变化需要重写时,在设计之初就可以使用工厂方法模式。

  2. 在设计一个连接服务器的框架时,需要多个协议,可以把这些协议作为产品类,实现统一的接口。


工厂方法模式的优缺点

其实优缺点就是对上面这些理论的总结,看不懂的小伙伴再品品上面的内容就行。


优点

  • 一个调用者想创建一个对象,只需要工厂名称即可。封装了产品的具体实现,调用者只用关注产品接口。

  • 符合开闭原则,这也是它和简单工厂模式最大的不同。扩展性高,如果想增加一个产品,只需要额外扩展一个工厂即可,不需要对原工厂进行任何变动。

  • 避免创建和使用的两部分代码的紧密耦合,使得代码更易维护。

缺点

工厂方法模式最大的问题是每次增加一个产品,就需要增加一个具体产品类和一个对应的具体工厂类。

这让整个程序中类的个数成倍增加,毫无疑问的增加系统的复杂度,新类的编译和运行还会给系统带来了额外的资源开销。

工厂方法模式的实现

这里的代码示例的思路源于程杰的《大话设计模式》,我们直接对比一下简单工厂模式和工厂方法模式的区别。


简单工厂模式

以简单的四则运算的计算器为例,先看看它的 UML 图。

通过代码去实现一下。

from abc import ABCMeta, abstractmethod


class Operation():
    """
    抽象产品类(运算符类)
    """

    __metaclass__ = ABCMeta

    def __init__(self):
        self.result = None

    @abstractmethod
    def GetResult(self):
        pass


class AddOperation(Operation):
    """
    具体产品类(加法运算符)
    """


    def GetResult(self, number1, number2):
        self.result = number1 + number2
        return self.result


class SubOperation(Operation):
    """
    具体产品类(减法运算符)
    """


    def GetResult(self, number1, number2):
        self.result = number1 - number2
        return self.result


class MulOperation(Operation):
    """
    具体产品类(乘法运算符)
    """


    def GetResult(self, number1, number2):
        self.result = number1 * number2
        return self.result


class DivOperation(Operation):
    """
    具体产品类(除法运算符)
    """


    def GetResult(self, number1, number2):
        if number2 == 0:
            print("分母不能为0")
            return self.result
        self.result = number1 / number2
        return self.result


class OperationFactory():
    """
    简单工厂类
    """

    @classmethod
    def CreateOperate(self, operator):
        oper = None
        if operator == "+":
            oper = AddOperation()
        elif operator == "-":
            oper = SubOperation()
        elif operator == "*":
            oper = MulOperation()
        elif operator == "/":
            oper = DivOperation()
        else:
            print("运算符输入错误")
        return oper

number1 = int(input("请输入数字:"))
operator = str(input("请输入运算符(+ - * /):"))
number2 = int(input("请输入数字:"))

oper = OperationFactory.CreateOperate(operator)
print("运算结果为:%.2f" % oper.GetResult(number1, number2))

工厂方法模式

接着我们用工厂方法模式实现四则运算,来瞅瞅 UML 图。

工厂方法模式就是一个工厂生产一类产品,借助代码去实现一下上述逻辑。

from abc import ABCMeta, abstractmethod


class Operation():
    """
    抽象产品类(运算符类)
    """

    __metaclass__ = ABCMeta

    def __init__(self):
        self.result = None

    @abstractmethod
    def GetResult(self):
        pass


class AddOperation(Operation):
    """
    具体产品类(加法运算符)
    """


    def GetResult(self, number1, number2):
        self.result = number1 + number2
        return self.result


class SubOperation(Operation):
    """
    具体产品类(减法运算符)
    """


    def GetResult(self, number1, number2):
        self.result = number1 - number2
        return self.result


class MulOperation(Operation):
    """
    具体产品类(乘法运算符)
    """


    def GetResult(self, number1, number2):
        self.result = number1 * number2
        return self.result


class DivOperation(Operation):
    """
    具体产品类(除法运算符)
    """


    def GetResult(self, number1, number2):
        if number2 == 0:
            print("分母不能为0")
            return self.result
        self.result = number1 / number2
        return self.result


class Factory():
    """
    通用工厂接口
    """

    __metaclass__ = ABCMeta

    @abstractmethod
    def CreateOperation(self):
        pass


class AddFactory(Factory):
    """
    具体工厂类(加法工厂类)
    """


    def CreateOperation(self):
        return AddOperation()


class SubFactory(Factory):
    """
    具体工厂类(减法工厂类)
    """


    def CreateOperation(self):
        return SubOperation()


class MulFactory(Factory):
    """
    具体工厂类(乘法工厂类)
    """


    def CreateOperation(self):
        return MulOperation()


class DivFactory(Factory):
    """
    具体工厂类(除法工厂类)
    """


    def CreateOperation(self):
        return DivOperation()


def main():
    number1 = int(input("请输入数字:"))
    operator = str(input("请输入运算符(+ - * /):"))
    number2 = int(input("请输入数字:"))

    if operator == "+":
        operFactory = AddFactory()
    elif operator == "-":
        operFactory = SubFactory()
    elif operator == "*":
        operFactory = MulFactory()
    elif operator == "/":
        operFactory = DivFactory()
    else:
        print("运算符输入错误")

    oper = operFactory.CreateOperation()
    print("运算结果为:%.2f" % oper.GetResult(number1, number2))


main()

对比两种设计模式,是不是工厂方法模式反而更复杂了。

如果简单工厂模式中添加一个新产品类,更改工厂类中的判断语句即可。

而工厂方法模式中添加一个新产品类,就还需要新增一个对应的工厂类,最后还需要修改客户端代码。

这就是两者最大的区别。

简单工厂模式中只有一个工厂类,最大的优势可能就是工厂类中包含有逻辑判断,根据客户端的选择创建相应的产品.

但增加一个新产品时,就需要大动干戈的去修改原有的工厂类,这就违反了开闭原则。

两者共有的问题就在于都需要修改客户端的代码,理论上可以通过反射解决避免分支判断的问题,这个我们之后再讲。


总结

工厂方法模式作为一种创建型模式,在任何需要生成复杂对象的地方,都可以用工厂方法模式。

但对于简单的对象,使用工厂方法模式只会徒增整个系统的复杂度,得不偿失。

还是那句话,任何设计模式的使用,都离不开具体需求具体分析。

举个例子,如果一段程序的工厂类和业务类(产品类)由两个小伙伴分开写,如果不用工厂方法模式,光沟通接口就得花上大量的时间,何苦呢!

不知不觉又到星期五了,这个星期小伙伴们有没有努力啊。猿哥给小伙伴们准备了一份 Python 入门到进阶的知识大纲,过段时间就发给你们,敬请期待哟。

看到最后的小伙伴如果认可我的内容,别忘了给猿哥点个【在看】,期待你与我的下一次相逢~


    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多