详解设计模式中的工厂方法模式在Python程序中的运用
作者:Blue Wish 发布时间:2022-11-14 01:06:58
工厂方法(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),因为它非常形象地描述了类的实例化过程,即负责"生产"一个对象。
简单说来,工厂方法模式的作用就是可以根据不同的条件生成各种类的实例,这些实例通常属于多个相似的类型,并且具有共同的父类。工厂方法模式将这些实例的创建过程封装了起来,从而简化了客户程序的编写,并改善了软件体系结构的可扩展性,使得将来能够以最小的代价加入新的子类。工厂方法这一模式适合在如下场合中运用:
当无法得知必须创建的对象属于哪个类的时候,或者无法得知属于哪个类的对象将被返回的时候,但前提是这些对象都符合一定的接口标准。
当一个类希望由它的子类来决定所创建的对象的时候,其目的是使程序的可扩展性更好,在加入其他类时更具弹性。
当创建对象的职责被委托给多个帮助子类(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这种动态语言来说,感觉体现不出太多价值,或许我还没有理解工厂方法模式的真谛。
猜你喜欢
- vim有各种强大的插件,这不仅归功于其提供的用来编写插件的脚本语言vimL,还得益于它良好的接口实现,从而支持python等语言编写插件。当
- 在SQL Server Management Studio 用WINDOWS连接的情况下改实列的“属性&rdqu
- 大家已经从实际使用中了解了jquery这个javascript框架的强大,其实jquery更加强大的是可扩展。你可以编写自己的基于jquer
- 1.sp_helpsort SELECT SERVERPROPERTY ('Collation') 查看你的排序规则. 不过
- 自动化处理PDF文件使用Python完成简单的PDF文件处理操作,如PDF文件的批量合并、拆分、加密以及添加水印等。1. 批量合并PDF文件
- 本文实例讲述了Python使用pickle模块储存对象操作。分享给大家供大家参考,具体如下:众所周知,当我们需要储存数据的时候,就需要用到重
- 无意中看到一位学员的屏保,感觉挺有意思的,就把它实现了下来效果如下:<!DOCTYPE html PUBLIC "-//W3
- 本文为大家分享了两段PHP分页类,很实用,供大家参考,具体内容如下<?php class Page { private $
- 联合结果集 新建临时工数据表 代码如下:CREATE TABLE T_TempEmployee (FIdCardNumber VARCHAR
- <?php $search = array ("'<script[^>]*?>.*?</sc
- 在绝大多数查询中,MySQL使用哪种校对规则进行比较是很显然的。例如,在下列情况中,校对规则明显的是“列x的列校对规则”:SELECT&nb
- 1、python-pptx模块简介使用python操作PPT,需要使用的模块就是python-pptx,下面来对该模块做一个简单的介绍。这里
- 工作时需要取得MySQL中一个表的字段是否存在于是就使用Describe命令来判断mysql_connect(localhost, root
- 统计在线人数是实时的吗?实现起来也比较简单,见下列代码:global.asa<SCRIPT LANGUAGE="V
- 每次抽取后都重新洗牌。计算10000次随机抽取可得到同花的几率。我做的比较复杂,分别累计了四种花色分别出现了几次import randoml
- 概述laravel服务容器就像一个高度自动化的工厂,你需要的东西,定制好模型,使用特定接口来制造。因为使用了服务容器,laravel中大部分
- 本文实例讲述了Python使用numpy产生正态分布随机数的向量或矩阵操作。分享给大家供大家参考,具体如下:简单来说,正态分布(Normal
- 在一般问题的优化中,最速下降法和共轭梯度法都是非常有用的经典方法,但最速下降法往往以”之”字形下降,速度较慢,不能很快的达到最优值,共轭梯度
- V5.0之后,我们总结了一些得失。首先要说的是改版的动力。产品设计或产品升级的驱动力只有两个:用户需求和网站目标。之前的我们的多次改版,其驱
- 0x00 字符的编码计算机毕竟是西方国家的发明,最开始并没有想到会普及到全世界,只用一个字节中的7位(ASCII)来表示字符对于现在庞大的文