详解设计模式中的工厂方法模式在Python程序中的运用


Posted in Python onMarch 02, 2016

工厂方法(Factory Method)模式又称为虚拟构造器(Virtual Constructor)模式或者多态工厂(Polymorphic Factory)模式,属于类的创建型模式。在工厂方法模式中,父类负责定义创建对象的公共接口,而子类则负责生成具体的对象,这样做的目的是将类的实例化操作延迟到子类中完成,即由子类来决定究竟应该实体化哪一个类。
在简单工厂模式中,一个工厂类处于对产品类进行实例化的中心位置上,它知道每一个产品类的细节,并决定何时哪一个产品类应当被实例化。简单工厂模式的优点是能够使客户端独立于产品的创建过程,并且在系统中引入新产品时无需对客户端进行修改,缺点是当有新产品要加入到系统中时,必须对工厂类进行修改,以加入必要的处理逻辑。简单工厂模式的致命弱点就是处于核心地位的工厂类,因为一旦它无法确定要对哪个类进行实例化时,就无法使用该模式,而工厂方法模式则可以很好地避免这一问题。
考虑这样一个应用程序框架(Framework),它可以用来浏览各种格式的文档,如TXT、DOC、PDF、HTML等,设计时为了让软件的体系结构能够尽可能地通用,定义了Application和Document这两个抽象父类,客户必须通过它们的子类来处理某一具体类型的文档。例如,要想利用该框架来编写一个PDF文件浏览器,必须先定义PDFApplication和PDFDocument这两个类,它们应该分别继承于Application和Document。
Application的职责是对Document进行管理,并且在需要时创建它们,比如当用户从菜单中选择Open或者New的时候,Application就要负责创建一个Document的实例。显而易见,被实例化的特定Document子类是与具体应用相关的,因此Application无法预测哪个Document的子类将被实例化,它只知道一个新的Document何时(When)被创建,但并不知道哪种(Which)具体的Document将被创建。此时若仍坚持使用简单工厂模式会出现一个非常尴尬的局面:框架必须实例化类,但它只知道不能被实例化的抽象类。
解决的办法是使用工厂方法模式,它封装了哪一个Document子类将被创建的信息,并且能够将这些信息从框架中分离出来。如图1所示,Application的子类重新定义了Application的抽象方法createDocument(),并返回某个恰当的Document子类的实例。我们称createDocument()是一个工厂方法(factory method),因为它非常形象地描述了类的实例化过程,即负责"生产"一个对象。

详解设计模式中的工厂方法模式在Python程序中的运用

简单说来,工厂方法模式的作用就是可以根据不同的条件生成各种类的实例,这些实例通常属于多个相似的类型,并且具有共同的父类。工厂方法模式将这些实例的创建过程封装了起来,从而简化了客户程序的编写,并改善了软件体系结构的可扩展性,使得将来能够以最小的代价加入新的子类。工厂方法这一模式适合在如下场合中运用:
当无法得知必须创建的对象属于哪个类的时候,或者无法得知属于哪个类的对象将被返回的时候,但前提是这些对象都符合一定的接口标准。
当一个类希望由它的子类来决定所创建的对象的时候,其目的是使程序的可扩展性更好,在加入其他类时更具弹性。
当创建对象的职责被委托给多个帮助子类(helper subclass)中的某一个,并且希望将哪个子类是代理者这一信息局部化的时候。
需要说明的是,使用工厂方法模式创建对象并不意味着一定会让代码变得更短(实事上往往更长),并且可能需要设计更多的辅助类,但它的确可以灵活地、有弹性地创建尚未确定的对象,从而简化了客户端应用程序的逻辑结构,并提高了代码的可读性和可重用性。

拿一个动物工厂来举例说明

class Animal(object):
  def eat(self, food):
    raise NotImplementedError()

class Dog(Animal):
  def eat(self, food):
    print '狗吃', food

class Cat(Animal):
  def eat(self, food):
    print '猫吃', food

class AnimalFactory(object):
  def create_animal(self):
    raise NotImplementedError()

class DogFactory(Animal):
  def create_animal(self):
    return Dog()

class CatFactory(AnimalFactory):
  def create_animal(self):
    return Cat()

def client():
  animal_factory = DogFactory()
  animal = animal_factory.create_animal()
  animal.eat('肉骨头')

  animal_factory = CatFactory()
  animal = animal_factory.create_animal()
  animal.eat('鱼骨头')

下面是简单工厂模式的实现:

class Animal(object):
  def eat(self, food):
    raise NotImplementedError()

class Dog(Animal):
  def eat(self, food):
    print '狗吃', food

class Cat(Animal):
  def eat(self, food):
    print '猫吃', food

def create_animal(name):
  if name == 'dog':
    return Dog()
  elif name == 'cat':
    return Cat()

def client():
  animal = create_animal('dog')
  animal.eat('肉骨头')
  animal = create_animal('cat')
  animal.eat('鱼骨头')

看起来工厂方法模式要复杂很多啊,也没觉得比简单工厂模式有什么好处,为什么还要用工厂方法模式呢? 简单工厂模式的优点很明显,工厂函数封装了逻辑判断,客户端使用负担要小很多。相应的问题也很明显,要增加新的产品类型,就需要修改工厂函数,这违背了开闭原则。 但是工厂方法模式似乎绕了一圈又回到原始时代了,下面写不就行了吗,何必外面套一层Factory:

class Animal(object):
  def eat(self, food):
    raise NotImplementedError()

class Dog(Animal):
  def eat(self, food):
    print '狗吃', food

class Cat(Animal):
  def eat(self, food):
    print '猫吃', food
def client():
  dog = Dog()
  dog.eat('肉骨头')

  cat = Cat()
  cat.eat('鱼骨头')

工厂方法模式,对于需要做强类型检查的语言比如Java、C++等在组织代码时是有好处的。对于Python这种动态语言来说,感觉体现不出太多价值,或许我还没有理解工厂方法模式的真谛。

Python 相关文章推荐
实例说明Python中比较运算符的使用
May 13 Python
简单介绍Python中用于求最小值的min()方法
May 15 Python
对python中矩阵相加函数sum()的使用详解
Jan 28 Python
pyqt5对用qt designer设计的窗体实现弹出子窗口的示例
Jun 19 Python
python对csv文件追加写入列的方法
Aug 01 Python
python程序 创建多线程过程详解
Sep 23 Python
Selenium+BeautifulSoup+json获取Script标签内的json数据
Dec 07 Python
详解Python中@staticmethod和@classmethod区别及使用示例代码
Dec 14 Python
Django2.1.7 查询数据返回json格式的实现
Dec 29 Python
python绘制高斯曲线
Feb 19 Python
python基于opencv批量生成验证码的示例
Apr 28 Python
Python使用Beautiful Soup(BS4)库解析HTML和XML
Jun 05 Python
探究python中open函数的使用
Mar 01 #Python
Saltstack快速入门简单汇总
Mar 01 #Python
通过Python使用saltstack生成服务器资产清单
Mar 01 #Python
Python解析json文件相关知识学习
Mar 01 #Python
使用简单工厂模式来进行Python的设计模式编程
Mar 01 #Python
Python文件处理
Feb 29 #Python
python 默认参数问题的陷阱
Feb 29 #Python
You might like
从MySQL数据库表中取出随机数据的代码
2007/09/05 PHP
PHP 文件缓存的性能测试
2010/04/25 PHP
php简单分页类实现方法
2015/02/26 PHP
详谈PHP编码转换问题
2015/07/28 PHP
javascript URL编码和解码使用说明
2010/04/12 Javascript
用javascript添加控件自定义属性解析
2013/11/25 Javascript
SeaJS 与 RequireJS 的差异对比
2014/12/08 Javascript
JavaScript中的Math.LOG2E属性使用详解
2015/06/14 Javascript
基于Jquery实现万圣节快乐特效
2015/11/01 Javascript
深入理解逻辑表达式的用法 与或非的用法
2016/06/06 Javascript
Vue.js快速入门教程
2016/09/07 Javascript
浅谈jquery之on()绑定事件和off()解除绑定事件
2016/10/26 Javascript
实现一个简单的vue无限加载指令方法
2017/01/10 Javascript
JavaScript实现音乐自动切换和轮播
2017/11/05 Javascript
微信小程序slider组件使用详解
2018/01/31 Javascript
node的process以及child_process模块学习笔记
2018/03/06 Javascript
创建echart多个联动的示例代码
2018/11/23 Javascript
js实现点击图片在屏幕中间弹出放大效果
2019/09/11 Javascript
layui默认选中table的CheckBox复选框方法
2019/09/19 Javascript
详解python的数字类型变量与其方法
2016/11/20 Python
Python 对象中的数据类型
2017/05/13 Python
python实现Excel文件转换为TXT文件
2019/04/28 Python
浅析CSS3 中的 transition,transform,translate之间区别和作用
2020/03/26 HTML / CSS
常用的HTML5列表标签
2017/06/20 HTML / CSS
美国护肤咨询及美容产品电商:Askderm
2017/02/24 全球购物
巴西化妆品商店:Lojas Rede
2019/07/26 全球购物
安全口号大全
2014/06/21 职场文书
计算机网络专业自荐信
2014/07/04 职场文书
党的群众路线教育实践活动先进个人材料
2014/12/24 职场文书
考察邀请函范文
2015/01/31 职场文书
毕业论文指导老师意见
2015/06/04 职场文书
新兵入伍决心书
2015/09/22 职场文书
教师廉政准则心得体会
2016/01/20 职场文书
解决pycharm下载库时出现Failed to install package的问题
2021/09/04 Python
vue中使用mockjs配置和使用方式
2022/04/06 Vue.js
MySQL添加索引特点及优化问题
2022/07/23 MySQL