详解设计模式中的工厂方法模式在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 05 Python
详解Django中的权限和组以及消息
Jul 23 Python
Python中的函数式编程:不可变的数据结构
Oct 08 Python
详解用python计算阶乘的几种方法
Aug 14 Python
python3的url编码和解码,自定义gbk、utf-8的例子
Aug 22 Python
Python通过Manager方式实现多个无关联进程共享数据的实现
Nov 07 Python
flask框架自定义过滤器示例【markdown文件读取和展示功能】
Nov 08 Python
Python函数式编程实例详解
Jan 17 Python
python PyAUtoGUI库实现自动化控制鼠标键盘
Sep 09 Python
Python生成九宫格图片的示例代码
Apr 14 Python
Python入门学习之类的相关知识总结
May 25 Python
一文搞懂Python Sklearn库使用
Aug 23 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
探讨如何使用SimpleXML函数来加载和解析XML文档
2013/06/07 PHP
newxtree.js代码
2007/03/13 Javascript
JQuery FlexiGrid的asp.net完美解决方案 dotNetFlexGrid-.Net原生的异步表格控件
2010/09/12 Javascript
基于jQuery实现的百度导航li拖放排列效果,即时更新数据库
2012/07/31 Javascript
jquery 如何动态添加、删除class样式方法介绍
2012/11/07 Javascript
extjs 04_grid 单击事件新发现
2012/11/27 Javascript
jquery入门—编写一个导航条(可伸缩)
2013/01/07 Javascript
JS/jQuery实现默认显示部分文字点击按钮显示全部内容
2013/05/13 Javascript
详细分析JavaScript函数定义
2015/07/16 Javascript
js实现将选中值累加到文本框的方法
2015/08/12 Javascript
基于OL2实现百度地图ABCD marker的效果
2015/10/01 Javascript
ion content 滚动到底部会遮住一部分视图的快速解决方法
2016/09/06 Javascript
node.js报错:Cannot find module 'ejs'的解决办法
2016/12/14 Javascript
js实现登录框鼠标拖拽效果
2017/03/09 Javascript
Vue 组件(component)教程之实现精美的日历方法示例
2018/01/08 Javascript
关于react-router/react-router-dom v4 history不能访问问题的解决
2018/01/08 Javascript
vue源码入口文件分析(推荐)
2018/01/30 Javascript
解决Layui选择全部,换页checkbox复选框重新勾选的问题方法
2018/08/14 Javascript
小程序实现页面顶部选项卡效果
2018/11/06 Javascript
JS实现求5的阶乘示例
2019/01/21 Javascript
Python中获取对象信息的方法
2015/04/27 Python
Python使用pickle模块存储数据报错解决示例代码
2018/01/26 Python
python实现得到当前登录用户信息的方法
2019/06/21 Python
利用Python库Scapy解析pcap文件的方法
2019/07/23 Python
Python产生一个数值范围内的不重复的随机数的实现方法
2019/08/21 Python
Python实现密码薄文件读写操作
2019/12/16 Python
HTML5实现自带进度条和滑块滑杆效果
2018/04/17 HTML / CSS
介绍一下javax.servlet.Servlet接口及其主要方法
2015/11/30 面试题
企业申诉管理制度
2014/01/30 职场文书
中学生自我鉴定
2014/02/04 职场文书
日语系毕业求职信
2014/07/27 职场文书
公务员群众路线专题民主生活会发言材料
2014/09/17 职场文书
档案工作个人总结
2015/03/03 职场文书
捐款仪式主持词
2015/07/04 职场文书
婚庆答谢词大全
2015/09/29 职场文书
mysql使用 not int 子查询隐含陷阱
2022/04/12 MySQL