Python 设计模式行为型访问者模式
作者:范桂飓 发布时间:2023-10-18 14:44:52
一、访问者模式(Visitor Pattern)
数据结构中保存着许多元素,当我们希望改变一种对元素的处理方式时,要避免重复的修改数据结构。那么就要求我们在实现代码时,将数据的处理进行分离,即:数据类只提供一个数据处理的接口,而该数据处理接口就被称之为访问者。那么,相同结构的数据面临不同的处理结果时,我们只需要创建不同的访问者。
访问者模式,指作用于一个对象结构体上的元素的操作。访问者可以使用户在不改变该结构体中的类的基础上定义一个新的操作。
优点:
使得在访问者类中针对复杂类结构中的某个类添加新方法较为容易,即:只需要简单地添加一个新的访问者方法即可。如果不采用访问者模式,这需要在每个类中添加一个新的方法。
访问者将相关的方法集中在一个具体的访问者类中,而其他相关的方法集中在另外一个具体的访问者类中。也就是说,访问者子类是按照方法的类型来分类的。
缺点:
增加一个具体的新
ConcreteElement
类比较困难。因为此时需要在每一个ConcreteVisitor
类中添加该ConcreteElement
类的访问方法。
二、应用场景
当一个对象的结构中,包含有多种类型的具有不同接口的对象,且用户要在这些对象上进行依赖于具体的类的运算时,需要用到访问者模式。这就是为什么访问者模式要针对每个被访问的子类都设计一个不同的接口的原因。事实上,如果每个被访问的子类都有相同的接口,包括构造方法、其他方法、参数都一致,则访问者类只需要设计一个访问方法,在该方法中含有一个用于区别不同的被访问的子类的参数即可。例如:可以使用被访问者基类作为参数类型。在对象的结构中包含有多种类型的有不同接口的对象时,各个不同的访问方法可能为访问所对应的类提供不同的参数类型。
当有多个不同的并且互不相关的运算将作用到这些对象上,而用户不希望这些运算混淆这些类时,可以使用访问者模式将相关的操作放到独立的类中,例如:为了实现每个结点类中的计算价格方法,可以将所有的计算价格方法放到一个 VisitPrice
类中。
在对象的数据类型很少改变,但是需要经常改变操作或者增加新的操作的情况下,可以使用访问者模式。反之,如果 Element
的子类经常改变结构,例如:需要增加一个新的税种,这就需要在访问者类中增加新的访问方法,因此,在这种情况下使用访问者模式代价较高,尽量不要使用访问者模式。
三、代码示例
该类图包含两个系列的类:“Element
类” 和 “访问者类”,访问者类定义了施加于 Element
类上的操作,为 Element 类提供一些功能。可以有多种具体的访问者类,各自完成特定的目的,如一个访问者类是计算价格,另一个访问者类则是计算存货数量。因此需要定义一个抽象的访问者父类 Visitor 以及用于各种特殊目的具体的子类。Visitor 类必须给每个结点类提供一个操作,即访问方法,例如获得各结点所代表的商品对象的价格等。
实体角色组成:
Visitor:为每个 Element 的对象声明一个访问操作。该访问操作的名字最好要包含被访问的类的名字,以便确认该访问操作是专门针对哪个具体的类,如:
visitFamilyNoChildren
是专门为了服务类 FamilyNoChildren 的。ConcreteVisitor:实现 Visitor 声明的运算。每个运算实现为对应的类的对象定义的算法的一部分。
ConcreteVisitor
提供算法的环境并且存储其局部状态。Element:定义了一些基本的方法,其中包含提供基本数据的方法,例如一些 get()与 set()方法。重要的是,每个 Element 子类都必须定义一个接收者方法,该方法以 Visitor 为参数类型:Accept(Visitor),其作用是为被访问者对象和访问者对象之间的交互提供接口。
ConcreteElement:具体的
Element
的子类,例如 ElementA,该类包含一个 accept 方法接收访问者对象。另外,该类还可能定义一些其他的方法以帮助访问者实现一些功能。ObjectStructure:提供一个高层接口,允许访问者访问 Element 的子类。在该类中可以包含一个结构,例如 ArrayList、Vector 等,提供所要访问的 element 的列表。
示例:上市公司的原始财务数据:
对于会计来说需要制作各种报表
对于财务总监来说需要分析公司业绩
对于战略顾问来说需要分析行业变化
class Finance:
"""财务数据结构类"""
def __init__(self):
self.salesvolume = None # 销售额
self.cost = None # 成本
self.history_salesvolume = None # 历史销售额
self.history_cost = None # 历史成本
def set_salesvolume(self, value):
self.salesvolume = value
def set_cost(self, value):
self.cost = value
def set_history_salesvolume(self, value):
self.history_salesvolume = value
def set_history_cost(self, value):
self.history_cost = value
def accept(self, visitor):
pass
class Finance_year(Finance):
"""2018 年财务数据类"""
def __init__(self, year):
Finance.__init__(self)
self.work = [] # 安排工作人员列表
self.year = year
def add_work(self, work):
self.work.append(work)
def accept(self):
for obj in self.work:
obj.visit(self)
class Accounting:
"""会计类"""
def __init__(self):
self.ID = "会计"
self.Duty = "计算报表"
def visit(self, table):
print('会计年度: {}'.format(table.year))
print("我的身份是: {} 职责: {}".format(self.ID, self.Duty))
print('本年度纯利润: {}'.format(table.salesvolume - table.cost))
print('------------------')
class Audit:
"""财务总监类"""
def __init__(self):
self.ID = "财务总监"
self.Duty = "分析业绩"
def visit(self, table):
print('会计总监年度: {}'.format(table.year))
print("我的身份是: {} 职责: {}".format(self.ID, self.Duty))
if table.salesvolume - table.cost > table.history_salesvolume - table.history_cost:
msg = "较同期上涨"
else:
msg = "较同期下跌"
print('本年度公司业绩: {}'.format(msg))
print('------------------')
class Adviser:
"""战略顾问"""
def __init__(self):
self.ID = "战略顾问"
self.Duty = "制定明年战略"
def visit(self, table):
print('战略顾问年度: {}'.format(table.year))
print("我的身份是: {} 职责: {}".format(self.ID, self.Duty))
if table.salesvolume > table.history_salesvolume:
msg = "行业上行,扩大生产规模"
else:
msg = "行业下行,减小生产规模"
print('本年度公司业绩: {}'.format(msg))
print('------------------')
class Work:
"""工作类"""
def __init__(self):
self.works = [] # 需要处理的年度数据列表
def add_work(self, obj):
self.works.append(obj)
def remove_work(self, obj):
self.works.remove(obj)
def visit(self):
for obj in self.works:
obj.accept()
if __name__ == '__main__':
work = Work() # 计划安排财务、总监、顾问对2018年数据处理
# 实例化2018年数据结构
finance_2018 = Finance_year(2018)
finance_2018.set_salesvolume(200)
finance_2018.set_cost(100)
finance_2018.set_history_salesvolume(180)
finance_2018.set_history_cost(90)
accounting = Accounting() # 实例化会计
audit = Audit() # 实例化总监
adviser = Adviser() # 实例化顾问
finance_2018.add_work(accounting) # 会计安排到2018分析日程中
finance_2018.add_work(audit) # 总监安排到2018分析日程中
finance_2018.add_work(adviser) # 顾问安排到2018分析日程中
work.add_work(finance_2018) # 添加2018年财务工作安排
work.visit()
来源:https://is-cloud.blog.csdn.net/article/details/122934106


猜你喜欢
- 在pytest自动化测试中,如果只是简单的从应用的角度来说,完全可以不去了解pytest中的显示信息的部分以及原理,完全可以通过使用推荐的p
- 在SQL Server数据库中如何查看一个登录名(login)的具体权限呢,如果使用SSMS的UI界面查看登录名的具体权限的话,用户数据库非
- 一、案例场景字段login_place,一共267725行记录,随机15条记录如下: 后续数据分析工作需要用到地理维度进行分析,所以需要把
- 开始使用MySQL 为关系型数据库(Relational Database Management System),一个关系型数据库由一个或数
- 高层的期望“3个月内,我希望网站能增加X注册用户,每日的独立IP到Y,网站盈利达到Z……”作为一个团队的领袖或者产品负责人,这样的期望是根据
- 问题起因最近要将一个文本分割成好几个topic,每个topic设计一个regressor,各regressor是相互独立的,最后汇总所有to
- 本文实例讲述了Go语言字典(map)用法。分享给大家供大家参考,具体如下:字典是一种内置的数据结构,用来保存 键值对 的 无序集合。(1)字
- 事先在网上搜索了一大圈,头都大了,看到那么多文章写道在python里安装psycopg2的各种坑和各种麻烦,各种不成功。搜索了一下午,索性外
- 本文实例讲述了Python3实现的反转单链表算法。分享给大家供大家参考,具体如下:反转一个单链表。方案一:迭代# Definition fo
- 前言因为经常一训练就是很多次迭代,所以找到效率比较高的操作能大大缩短运行时间,但这方面资料不足,所以自己记录总结一下,有需要再补充索引效率与
- 在实际工作中,无论是对数据库系统(DBMS),还是对数据库应用系统(DBAS),查询优化一直是一个热门话题。一个成功的数据库应用系统的开发,
- 此代码适合你做网站用,普通朋友可以不用理这个东西!ASP:<%dim objXMLHTTP, qq, pwd qq = &
- 前言:二分法也就是二分查找,它是一种效率较高的查找方法假如公司新来了一个人,叫张三,他是你们公司第47个人,过了一段时间后,有些人呢看张三不
- 目录 一、前言1.1 什么是 import 机制?1.2 import 是如何执行的?二、import 机制概览三、import
- 1、其中再语义分割比较常用的上采样:其实现方法为:def upconv2x2(in_channels, out_channels, mode
- 本文实例讲述了Python中itertools模块用法,分享给大家供大家参考。具体分析如下:一般来说,itertools模块包含创建有效迭代
- 优先队列的二叉堆实现在前面的章节里我们学习了“先进先出”(FIFO)的数据结构:队列(Queue)。队列有一种变体叫做“优先队列”(Prio
- Hello every, 我是Sunrise_Chen,有人知道我吗?好久没来这里了,以前偶尔会来这里潜水今天心情很好,写了几个特效果。特效
- 网络I/O模型人多了,就会有问题。web刚出现的时候,光顾的人很少。近年来网络应用规模逐渐扩大,应用的架构也需要随之改变。C10k的问题,让
- 一、运行vue项目1、下载node.js安装完成后分别在cmd中执行node -v查看是否安装成功,出现版本号就安装成功了2、安装 webp