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

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

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

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

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

什么是工厂方法模式

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

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

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

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

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

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

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

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

但它不属于 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)

相关推荐

  • 金三银四,50道必备的Python面试题(建议收藏)

    在过去的2020年,Python赢得了年度TIOBE编程语言奖,成为过去一年最受欢迎的编程语言.在数据科学和机器学习等领域中,被广泛使用. 正值"金三银四"招聘季,小F给大家整理了 ...

  • 设计模式——把类作为参数的抽象工厂模式

    今天给大家介绍一个非常简单的设计模式,一学就会,非常好用. 这个模式叫做抽象工厂模式,大家可能对工厂模式比较熟悉,在工厂模式当中封装了实例的创建逻辑.主要的用途一般是将一些复杂的类的创建过程整合在一起 ...

  • Python语言学习:python语言的特点、入门、基础用法之详细攻略

    Python语言学习:python语言的特点.入门.基础用法之详细攻略 相关内容 Python 基础教程 python语言的特点 1.pickle功能:Python 有pickle 这个便利的功能.这 ...

  • 简单工厂模式

    模拟一个简单的需求 设计一个简单的计算器,能够完成加减乘除的操作,并且能够设置运算的两个操作数(暂时不考虑计算器的界面). 分析 思路1 可以创建一个计算器类,里面包含加减乘除的运算,以及设置获取操作 ...

  • 设计模式学习笔记 ———— 简单工厂模式

    # 背景 简单工厂模式是很多程序员学习的第一个设计模式,因为其不但原理简单而且易于上手,在日常工作的代码中也常有体现.今天分享一个基于实现"加"."减".&qu ...

  • 边想边写: 程序

    程序 文/海拔3500米 什么事每天重复却不得不做 一是吃饭,二是睡觉 人是这样,猪也是这样 甚至鸡.马.猫.狮子.老虎.苍蝇.蚂蚁 其实动物都是这样 甚至植物也是这样 梦里的场景出现在现实中 先知还 ...

  • 总结90条写Python程序的建议!

    作者丨笑虎来源丨http://zhuanlan.zhihu.com/p/32817459 自己写 Python 也有四五年了,一直是用自己的"强迫症"在维持自己代码的质量.都有去看 ...

  • 写PLC程序从这四个方面入手,质量都很高

    欢迎转发朋友圈,欢迎文末留言 一.PLC程的设计要求 一套完整的PLC程序,并不仅仅是使系统能够运行起来这么简单,它也需要完整的注释.精良的架构.良好的可扩展性.完备的报警保护系统.运行前的模拟系统. ...

  • 高手写PLC程序,只注重这两点!

    电气系 1周前 PLC程序的内容 PLC应用程序应最大限度地满足被控对象的控制要求,在构思程序主体的框架后,要以它为主线,逐一编写实现各控制功能或各子任务的程序.经过不断他调整和完善.使程序能完成所要 ...

  • 总结了 90 条写 Python 程序的建议

    本文于网络整理,版权归原作者所有 阅读文本大概需要 5 分钟 这是一名5年经验的 Python小伙伴总结的90条python建议,如果你也在学pythpn,准备要学习python,希望这篇文章对你有用 ...

  • 初学者进阶:你真的会写PLC程序吗?

    很多人对学习PLC理解上有一个误区,那就是以为会写程序能实现功能就可以了.所以学习过程中只注重PLC编程软件的应用.其实要想学会做一个PLC项目,写程序只是其中一个环节.除此之外,系统配置.控制柜成套 ...

  • 写PLC程序注意这两点,工资翻倍!!!

    因为情怀还未了 所以四海为家 欢迎转发朋友圈,欢迎收藏   对于PLC程序的编写,我认为无须天马行空的标新立异,也无须花枝招展的炫弄技巧.只要在内容和质量这两方面把好关,具有正确性.可靠性.方便性.简 ...

  • 你写的程序很健壮?不妨测一下?

    这是一篇测试相关的笔记.我们软件开发最终都离不开测试的,可以通过测试来发现很多问题.在这之前先扯谈一波: 在这给还没找工作的朋友提个醒,能找开发的职位就别找测试的职位,除非你真的很喜欢测试.亲身经历, ...

  • 程序员奇谈之我写的程序不可能有bug篇

    程序员在普通人的印象里是一份严(ku)谨(bi)的职业,也是一个被搞怪吐槽乐此不疲的职业,程序员们面对复杂的代码敲打电脑时连眉头都不会皱一下,但是有一个词却是他们痛苦的根源,它就是Bug. 有不少的新 ...