github地址:https://github.com/cheesezh/python_design_patterns

简单工厂模式 v.s. 工厂方法模式

以简单计算器为例,对比一下简单工厂模式和工厂方法模式的区别。

简单工厂模式

from abc import ABCMeta, abstractmethod

class Operation():
"""
抽象产品类(运算符类)
"""
__metaclass__ = ABCMeta def __init__(self):
self.result = None @abstractmethod
def get_result(self):
pass class AddOperation(Operation):
"""
具体产品类(加法运算符)
"""
def get_result(self, number_a, number_b):
self.result = number_a + number_b
return self.result class SubOperation(Operation):
"""
具体产品类(减法运算符)
"""
def get_result(self, number_a, number_b):
self.result = number_a - number_b
return self.result class MulOperation(Operation):
"""
具体产品类(乘法运算符)
"""
def get_result(self, number_a, number_b):
self.result = number_a * number_b
return self.result class DivOperation(Operation):
"""
具体产品类(除法运算符)
"""
def get_result(self, number_a, number_b):
if number_b == 0:
print("With operator '/', the second number can not be zero.")
return self.result
self.result = number_a / number_b
return self.result class OperationFactory():
"""
产品工厂类
"""
@classmethod
def create_operate(self, operator):
oper = None
if operator == "+":
oper = AddOperation()
elif operator == "-":
oper = SubOperation()
elif operator == "*":
oper = MulOperation()
elif operator == "/":
oper = DivOperation()
else:
print("Wrong operator.")
return oper number_a = int(input("input a number:"))
operator = str(input("input a operater(+ - * /):"))
number_b = int(input("input a number:")) oper = OperationFactory.create_operate(operator)
print(oper.get_result(number_a, number_b))
input a number:99
input a operater(+ - * /):/
input a number:9
11.0

工厂方法模式

from abc import ABCMeta, abstractmethod

class IFactory():
"""
通用工厂接口
"""
__metaclass__ = ABCMeta @abstractmethod
def create_operation(self):
pass class AddFactory(IFactory):
"""
实现工厂接口的加法工厂类
"""
def create_operation(self):
return AddOperation() class SubFactory(IFactory):
"""
实现工厂接口的剑法工厂类
"""
def create_operation(self):
return SubOperation() class MulFactory(IFactory):
"""
实现工厂接口的乘法工厂类
"""
def create_operation(self):
return MulOperation() class DivFactory(IFactory):
"""
实现工厂接口的除法工厂类
"""
def create_operation(self):
return DivOperation() def main():
number_a = int(input("input a number:"))
operator = str(input("input a operater(+ - * /):"))
number_b = int(input("input a number:")) if operator == "+":
oper_factory = AddFactory()
elif operator == "-":
oper_factory = SubFactory()
elif operator == "*":
oper_factory = MulFactory()
elif operator == "/":
oper_factory = DivFactory()
else:
print("Wrong operator.") oper = oper_factory.create_operation()
print(oper.get_result(number_a, number_b)) main()
input a number:99
input a operater(+ - * /):/
input a number:11
9.0

点评

工厂方法更复杂了?

如果需要增加其他运算,比如求M的N次方。

在简单工厂模式里,先增加一个求M的N次方的产品类,然后更改工厂类的if判断增加分支即可。

在工厂方法模式里,先增加一个求M的N次方的产品类,还要新增一个相关工厂类,最后还有修改客户端代码。

这就是简单工厂和工厂方法的区别所在。简单工厂模式的最大优点在于工厂类中包含了必要的逻辑判断,根据客户端的选择条件动态实例化相关类,对于客户端来说,去除了与具体产品的依赖。但是,如果要增加一个新的功能,比如求M的N次方,需要更改工厂类的if判断分支条件,修改原有的类?违背了开放-封闭原则,这可不是好方法。所以就需要工厂方法模式来处理。

工厂方法模式

工厂方法模式,定义一个用于创建对象的接口,让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到其子类[DP]。

相当于将简单工厂中的工厂类,变成了一个工厂抽象接口和多个具体生成对象的工厂,于是我们要增加求M的N次方的功能,就不需要更改工厂类,只需要增加此功能的运算类和相应的工厂类即可。这样整个工厂和产品体系其实都没有修改,而只是扩展,这就完全符合了开放-封闭原则。

但是,工厂方法模式是现实,客户端需要决定实例化哪一个工厂来实现运算类,选择判断的问题还是存在的,也就是说,工厂方法把简单工厂的内部逻辑判断移到了客户端代码来进行。要增加新功能,本来修改工厂类,现在修改客户端了。

题目

木叶学校组织学雷锋活动,让鸣人,小樱,佐助帮敬老院的老人扫地,洗衣,买米,如何实现?

class LeiFeng():

    def sweep(self):
print("扫地") def wash(self):
print("洗衣") def buy_rice(self):
print("买米") class Student(LeiFeng):
pass def main():
mingren = Student()
xiaoying = Student()
zuozhu = Student() mingren.sweep()
xiaoying.wash()
zuozhu.buy_rice()
main()
扫地
洗衣
买米

点评

  • 学生都会毕业,但是帮助老人是长期工作,所以每次不同的人帮助老人,都需要改客户端代码,而且老人不可能知道所有来帮忙的学生的名字;
  • 除了学生,社区志愿者也可以帮助老人

如何用简单工厂方法解决上述问题?

class Volunteer(LeiFeng):
pass class SimpleFactory(): @classmethod
def create_leifeng(self, leifeng_type):
self.leifeng = None
if leifeng_type == "学生":
self.leifeng = Student()
elif leifeng_type == "志愿者":
self.leifeng = Volunteer()
else:
print("ERROR LeiFeng Type")
return self.leifeng def main():
studentA = SimpleFactory.create_leifeng("学生")
studentA.buy_rice()
studentB = SimpleFactory.create_leifeng("学生")
studentB.wash()
studentC = SimpleFactory.create_leifeng("学生")
studentB.sweep() main()
买米
洗衣
扫地

点评

  • 好的地方,客户端的代码,如果要换志愿者,只需要换参数即可;
  • 坏的地方,在任何实例化的时候都需要写一句SimpleFactory.create_leifeng("学生"),这会导致大量重复,在修改为志愿者的时候非常麻烦,可以用工厂方法解决这个问题;
from abc import ABCMeta, abstractmethod

class ILeiFengFactory():
__metaclass__ = ABCMeta @abstractmethod
def create_leifeng(self):
pass class StudentFactory(ILeiFengFactory): def create_leifeng(self):
return Student() class VolunteerFactory(ILeiFengFactory): def create_leifeng(self):
return Volunteer() def main():
leifeng_factory = StudentFactory()
stu1 = leifeng_factory.create_leifeng()
stu2 = leifeng_factory.create_leifeng()
stu3 = leifeng_factory.create_leifeng() stu1.sweep()
stu2.wash()
stu3.buy_rice() main()
扫地
洗衣
买米

点评

  • 此时如果要将学生改成志愿者,只需要修改一行代码即可;
  • 工厂方法克服了简单工厂违背开放-封闭原则的缺点,又保持了封装对象创建过程的优点;

总结

简单工厂和工厂方法都是集中封装了对象的创建,使得要更换对象时,不需要做大的改动就可以实现,降低了客户程序和产品对象的耦合。

工厂方法是简单工厂模式的进一步抽象和推广,由于使用了多态性,工厂方法模式保持了简单工厂模式的优点,而且克服了它的缺点。但缺点是由于每加一个产品,就需要加一个产品工厂类,增加了额外开发量。

另外,工厂方法还是没有避免修改客户端的代码,可以利用反射解决避免分支判断的问题。

[Python设计模式] 第8章 学习雷锋好榜样——工厂方法模式的更多相关文章

  1. 设计模式C#实现(九)——工厂方法模式和简单工厂

    工厂方法模式:定义一个用于创建对象的接口,让子类决定实例化哪一个类.Factory Method使一个类的实例化延迟到其子类. 构成: 1.Product工厂方法创建的对象的接口 2.Concrete ...

  2. Java设计模式从精通到入门四 工厂方法模式

    工厂方法模式 属于23中设计模式中创建型类型. 核心思想:工厂提供创建对象的接口,由子类决定实例化哪一个子类. 来源 ​ 设计模式之禅中的例子,女娲造人,通过八卦炉来进行造人,没有烧熟的为白人,烧太熟 ...

  3. PHP学习之工厂方法模式

    <?php //工厂方法模式 interface Doing { function eat(); function sleep(); } class Cat implements Doing { ...

  4. [Python设计模式] 第1章 计算器——简单工厂模式

    github地址:https://github.com/cheesezh/python_design_patterns 写在前面的话 """ 读书的时候上过<设计模 ...

  5. [Python设计模式] 第28章 男人和女人——访问者模式

    github地址:https://github.com/cheesezh/python_design_patterns 题目 用程序模拟以下不同情况: 男人成功时,背后多半有一个伟大的女人: 女人成功 ...

  6. [Python设计模式] 第24章 加薪审批——职责链模式

    github地址:https://github.com/cheesezh/python_design_patterns 题目 用程序模拟以下情景 员工向经理发起加薪申请,经理无权决定,需要向总监汇报, ...

  7. [Python设计模式] 第20章 挨个买票——迭代器模式

    github地址:https://github.com/cheesezh/python_design_patterns 迭代器模式 迭代器模式,提供一种方法顺序访问一个聚合对象中各个元素,而又不暴露该 ...

  8. [Python设计模式] 第18章 游戏角色备份——备忘录模式

    github地址:https://github.com/cheesezh/python_design_patterns 题目 用代码模拟以下场景,一个游戏角色有生命力,攻击力,防御力等数据,在打Bos ...

  9. C#设计模式之二工厂方法模式(Factory Method Pattern)【创建型】

    一.引言 在上一篇文章中我们讲解了过渡的一种模式叫做[简单工厂],也有叫[静态工厂]的,通过对简单工厂模式得了解,我们也发现了它的缺点,就是随着需求的变化我们要不停地修改工厂里面的方法的代码,需求变化 ...

随机推荐

  1. X分钟速成Python

    先附上一张号称“一篇程序覆盖Python基础的代码”,基本的结构都涉及到了 源代码下载: learnpython-zh.py Python 由 Guido Van Rossum 在90年代初创建. 它 ...

  2. 为什么NULL指针也能访问成员函数?(但不能访问成员变量)

    查看更加详细的解析请参考这篇文章:http://blog.51cto.com/9291927/2148695 看一个静态绑定的例子: 1 #include <iostream> 2 3 u ...

  3. 针对SSL/TLS的拒绝服务攻击以及使用ettercap进行DNS欺骗

    一. thc-ssl-dos 1.简介 (1).SSL 协商加密对性能开销增加,大量握手请求会导致 DOS (2).利用 SSL secure Renegotiation 特性,在单一 TCP 连接中 ...

  4. Oracle - 全角和半角

    全角和半角 只有字符,数字,标点符号有全角和半角的区别,中文没有 普通写一句英文(即不要输入法):I am 28 years old, do you love me?采用半角写:I am 28 yea ...

  5. muduo网络库架构总结

    目录 muduo网络库简介 muduo网络库模块组成 Recator反应器 EventLoop的两个组件 TimerQueue定时器 Eventfd Connector和Acceptor连接器和监听器 ...

  6. hystrix dashboard Unable to connect to Command Metric Stream解决办法

    spring cloud 在初次使用 hystrix dashboard仪表盘的时候很容易出现hystrix dashboard Unable to connect to Command Metric ...

  7. postcss那些事儿

    一.sass和postcsssass.less.stylus,三者是css预处理器,使用预编码来处理css,然后编译成线上需要的css.postcss是处理你写好的css,让你的css更健康,对浏览器 ...

  8. hibernate中多对一问题

    例如User类中有个Group引用对应数据库就是groupId的外键了. 外键映射成一个类对吧. <many-to-one name="groupId" column=&qu ...

  9. Node+Express+MongoDB + Socket.io搭建实时聊天应用实战教程(二)--node解析与环境搭建

    前言 本来开始写博客的时候只是想写一下关于MongoDB的使用总结的,后来觉得还不如干脆写一个node项目实战教程实战.写教程一方面在自己写的过程中需要考虑更多的东西,另一方面希望能对node入门者有 ...

  10. Java并发程序设计(九)设计模式与并发之不变模式

    设计模式与并发之不变模式 使用不变模式的目的:除去多线程中的同步操作,提高并行程序的性能. 一个类在的内部状态创建后,在整个生命周期内都不会发生改变,该类就是不变类. /** * @author: T ...