【通俗说设计模式】二、工厂方法模式(Factory Method Pattern)& Python实现

专业介绍

   工厂方法模式(英语:Factory method pattern)是一种实现了“工厂”概念的面向对象设计模式。就像其他创建型模式一样,它也是处理在不指定对象具体类型的情况下创建对象的问题。工厂方法模式的实质是“定义一个创建对象的接口,但让实现这个接口的类来决定实例化哪个类。工厂方法让类的实例化推迟到子类中进行。引用自--维基百科

       核心结构:有四个角色,分别是抽象工厂;具体工厂;抽象产品;具体产品 。

       关键字:定义接口、推迟实例化

:下文中的类是一个统称,可以是Java中的接口,也可以是Python中的class、function

通俗介绍

    它是简单工厂模式的衍生,解决了后者的一些问题,符合开闭原则(对内修改关闭,对外扩展开放)。 在之前的简单工厂模式中多个产品共有一个工厂对象,那么当引进一个新产品时,必然要修改这个共有的工厂对象以支持新产品,修改就可能导致以前的代码出现bug,不够优雅和严谨!而在工厂方法模式下,不仅抽离出了抽象的产品类,还把工厂类都抽象出来了,一个抽象工厂对应一个抽象产品,往后都是基于这个抽象产品和抽象工厂进行扩展。

*注:工厂方法模式由于场景较为少见所以多出现于工具库和框架里面,平常的业务代码中用的更多的是简单工厂模式

具体模式使用步骤:

       1. 创建抽象工厂:定义工厂类所拥有的方法/属性

       2. 创建抽象产品:定义最终产品所拥有的方法/属性

       3. 创建具体产品:继承抽象产品,主要创建具体产品的方法/属性,必须按照抽象产品的定义来创建

       4. 创建具体工厂:继承抽象工厂,主要创建 "生产具体产品的工厂类",必须按照抽象工厂的定义来创建

本质:将工厂类抽象出来,使其可扩展,这样在扩展产品时,也能扩展对应的工厂,而无需修改已有的工厂来支持。简单工厂模式与它的惟一的不同就是前者只有一个工厂,简单的同时也具有较低的扩展性。

 

问题1:工厂方法模式中如何扩展(引进新产品)呢?

答案:新增一个A产品类(继承抽象产品类),再新增一个对应的A工厂类(继承抽象工厂类)只负责生产(创建)A产品。

 

问题2:工厂方法模式的适用场景是什么?

答案:某一种具有多种属性(attr, method)的对象(比如汽车),有不同的实体类型(各种品牌的汽车),它们的创建逻辑具有较大的差异,且以后很可能扩展新的实体。

这里的描述确实很抽象,但这可能是我能想到的唯一能够完整描述这种模式场景的话语了。再举个例子加深理解,程序用到一种American对象和Chinese对象,他们都有eat(),sleep()方法,还有skin/habit等属性。显然这两种对象的方法和属性的具体内容都是不同的,而且未来可能增加Spanish,Russian对象,它们都具有几乎 ”相同“ 的方法和属性,但具体内容方式都不同。这个时候要如何设计出可靠、优雅的代码结构呢?答案就是工厂方法模式,下面的代码示例将以这个场景为例。

 

问题3:工厂方法模式有什么缺点?

答案:每引进一个产品,就要增加两个类,一个是具体的产品类,一个是负责创建这个产品的工厂类,会增加复杂度。

代码示例

  下面的代码是问题2的具体实现

# coding=utf-8

import abc, six
from datetime import datetime


# step-1: 创建抽象工厂
@six.add_metaclass(abc.ABCMeta)
class AbstractFactory:
    @abc.abstractmethod
    def produce_people(self, name):
        pass


# step-2: 创建抽象产品
@six.add_metaclass(abc.ABCMeta)
class People:
    name = ''
    skin = ''
    habit = []

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

    @abc.abstractmethod
    def eat(self, item):
        pass

    @abc.abstractmethod
    def sleep(self):
        pass


# step-3: 创建具体产品
# 具体产品 Chinese
class Chinese(People):
    skin = 'yellow'
    habit = ['learn', 'work']

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

    def eat(self, item):
        if item in ['Coffee', 'Hamburger']:
            print('I like it!')
        else:
            print('What the hell is this? let it away from me!')

    def sleep(self):
        if datetime.now().hour > 11 or datetime.now().hour < 7:
            print('I am sleeping!')
        else:
            print('I am awake!')


# 具体产品 American
class American(People):
    skin = 'white'
    habit = ['learn', 'work']

    def eat(self, item):
        if item in ['Coffee', 'Hamburger']:
            print('I like it!')
        else:
            print('What the hell is this? let it away from me!')

    def sleep(self):
        if datetime.now().hour > 11 or datetime.now().hour < 7:
            print('I am sleeping!')
        else:
            print('I am awake!')


# step-4: 创建Chinese的具体工厂
class ChineseFactory(AbstractFactory):
    def produce_people(self, name):
        return Chinese(name)


# American的具体工厂
class AmericanFactory(AbstractFactory):
    def produce_people(self, name):
        return American(name)


if __name__ == '__main__':
    chineseFactory = ChineseFactory()  # 先实例化chinese工厂
    chineseA = chineseFactory.produce_people('A')  # 再生产 chineseA
    chineseB = chineseFactory.produce_people('B')  # 再生产 chineseA

    americanFactory = AmericanFactory()  # 先实例化american工厂
    americanA = americanFactory.produce_people('A')  # 再生产 americanA
    americanB = americanFactory.produce_people('B')  # 再生产 americanB
    # 实际场景中,在某个位置一般只会用一个工厂,多个工厂是为了满足多场景

 工厂方法遵循了以下原则:

       0. 开闭原则:对内修改关闭,对外修改开放

       1. 里氏替换原则:继承的时候不要修改父类的定义好的功能

       2. 依赖倒置原则:应该是具体依赖抽象,而不是反过来。抽象定义不应该包含细节,抽象的定义应该足够简单。

       3. 单一职责原则:一个类应该只具有一个(或尽可能少的)职责,这样才能更好地应对变化。也能提高可读性,可维护性。

  除这些外,还有接口隔离原则:降低别的类对这个类的接口依赖,指定义类中的单个接口时,不应包含过多的功能,尽量将大的接口拆分为更小的接口,让接口中只包含调用者想要的内容。才能降低单个接口的被依赖性,才可以预防外来的变更的扩散。但是工厂方法模式中没太多体现。

后面

     与简单工厂方法不一样的是,工厂方法具有更高程度的抽象性,在平时的业务开发中可能极少会需要用到(简单工厂方法用的多一点),更多的是在工具库/大型框架中会用到。如果你经常查看一些web或数据库框架源码,那你应该是看到过的。掌握抽象能力是开发者进阶的必经之路,如果不善于抽象,那就是真的码农了。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值