[Python设计模式] 第8章 学习雷锋好榜样——工厂方法模式
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章 学习雷锋好榜样——工厂方法模式的更多相关文章
- 设计模式C#实现(九)——工厂方法模式和简单工厂
工厂方法模式:定义一个用于创建对象的接口,让子类决定实例化哪一个类.Factory Method使一个类的实例化延迟到其子类. 构成: 1.Product工厂方法创建的对象的接口 2.Concrete ...
- Java设计模式从精通到入门四 工厂方法模式
工厂方法模式 属于23中设计模式中创建型类型. 核心思想:工厂提供创建对象的接口,由子类决定实例化哪一个子类. 来源 设计模式之禅中的例子,女娲造人,通过八卦炉来进行造人,没有烧熟的为白人,烧太熟 ...
- PHP学习之工厂方法模式
<?php //工厂方法模式 interface Doing { function eat(); function sleep(); } class Cat implements Doing { ...
- [Python设计模式] 第1章 计算器——简单工厂模式
github地址:https://github.com/cheesezh/python_design_patterns 写在前面的话 """ 读书的时候上过<设计模 ...
- [Python设计模式] 第28章 男人和女人——访问者模式
github地址:https://github.com/cheesezh/python_design_patterns 题目 用程序模拟以下不同情况: 男人成功时,背后多半有一个伟大的女人: 女人成功 ...
- [Python设计模式] 第24章 加薪审批——职责链模式
github地址:https://github.com/cheesezh/python_design_patterns 题目 用程序模拟以下情景 员工向经理发起加薪申请,经理无权决定,需要向总监汇报, ...
- [Python设计模式] 第20章 挨个买票——迭代器模式
github地址:https://github.com/cheesezh/python_design_patterns 迭代器模式 迭代器模式,提供一种方法顺序访问一个聚合对象中各个元素,而又不暴露该 ...
- [Python设计模式] 第18章 游戏角色备份——备忘录模式
github地址:https://github.com/cheesezh/python_design_patterns 题目 用代码模拟以下场景,一个游戏角色有生命力,攻击力,防御力等数据,在打Bos ...
- C#设计模式之二工厂方法模式(Factory Method Pattern)【创建型】
一.引言 在上一篇文章中我们讲解了过渡的一种模式叫做[简单工厂],也有叫[静态工厂]的,通过对简单工厂模式得了解,我们也发现了它的缺点,就是随着需求的变化我们要不停地修改工厂里面的方法的代码,需求变化 ...
随机推荐
- python 全栈开发,Day81(博客系统个人主页,文章详情页)
一.个人主页 随笔分类 需求:查询当前站点每一个分类的名称以及对应的文章数 完成这个需求,就可以展示左侧的分类 它需要利用分组查询,那么必须要会基于双下划线的查询. 基于双下划线的查询,简单来讲,就是 ...
- Math对象的常用属性和方法
属性 描述 Math.PI 返回π(3.1415926) 方法 描述 Math.round() 将数字四舍五入到离它最近的整数 Math.sart(n) 返回平方根,例如Math.sart(9)返回3 ...
- BBC 记录片planet earth
He'll have to remain on guard for another two weeks, but in the jungle, just surviving the day can c ...
- 《转》理解restful
越来越多的人开始意识到,网站即软件,而且是一种新型的软件. 这种"互联网软件"采用客户端/服务器模式,建立在分布式体系上,通过互联网通信,具有高延时(high latency).高 ...
- RabbitMQ(一):RabbitMQ 安装与配置(Mac)
一.rabbitmq 安装与配置 安装: brew install rabbitmq # 进入安装目录 cd /usr/local/Cellar/rabbitmq/3.7.12 # 启动 brew s ...
- .NET正则平衡组
1 概述 平衡组是微软在.NET中提出的一个概念,主要是结合几种正则语法规则,提供对配对出现的嵌套结构的匹配..NET是目前对正则支持最完备.功能最强大的语言平台之一,而平衡组正是其强大 ...
- hihocoder 1341 Constraint Checker【string】
hihocoder 1341 解释:这道题题目还是比较容易理解,就是根据输入的若干个不等式,校验后面输入的数据是否都满足前面的不等式,满足就输出Yes,只要有一个不满足就输出No.如“A<B&l ...
- 洛谷 P1162 填涂颜色【DFS】
题目链接:https://www.luogu.org/problemnew/show/P1162 题目描述 由数字 0 组成的方阵中,有一任意形状闭合圈,闭合圈由数字 1 构成,围圈时只走上下左右 4 ...
- pandas学习(创建多层索引、数据重塑与轴向旋转)
pandas学习(创建多层索引.数据重塑与轴向旋转) 目录 创建多层索引 数据重塑与轴向旋转 创建多层索引 隐式构造 Series 最常见的方法是给DataFrame构造函数的index参数传递两个或 ...
- Java中设置方法执行的超时时间java.util.concurrent.Future
java.util.concurrent.Future Future代表一个异步计算的结果.它提供了方法来检查是否计算已经完成,还是正在计算而处于等待状态,并且也提供了获取计算结果 方法.当计算完成后 ...