设计模式Python版 访问者模式

文章目录

  • 前言
  • 一、访问者模式
  • 二、访问者模式示例


前言

GOF设计模式分三大类:

  • 创建型模式:关注对象的创建过程,包括单例模式、简单工厂模式、工厂方法模式、抽象工厂模式、原型模式和建造者模式。
  • 结构型模式:关注类和对象之间的组合,包括适配器模式、桥接模式、组合模式、装饰模式、外观模式、享元模式和代理模式。
  • 行为型模式:关注对象之间的交互,包括职责链模式、命令模式、解释器模式、迭代器模式、中介者模式、备忘录模式、观察者模式、状态模式、策略模式、模板方法模式和访问者模式。

一、访问者模式

访问者模式(Visitor Pattern)

  • 定义:提供一个作用于某对象结构中的各元素的操作表示,它使得可以在不改变各元素的类的前提下定义作用于这些元素的新操作。

  • 解决问题:如何操作一个包含多种类型对象的复杂结构?

  • 使用场景:

    • 当系统中存在一个较为复杂的对象结构,且不同访问者对其所采取的操作也不相同时,可以考虑使用访问者模式进行设计。
    • 一个对象结构包含多种类型的对象,希望对这些对象实施一些依赖其具体类型的操作。在访问者中针对每一种具体的类型都提供了一个访问操作,不同类型的对象可以有不同的访问操作。
  • 组成:

    • Visitor(抽象访问者):抽象访问者为对象结构中每个具体元素类ConcreteElement声明一个访问操作,从这个操作的名称或参数类型可以清楚知道需要访问的具体元素的类型。
    • ConcreteVisitor(具体访问者):具体访问者实现了每个由抽象访问者声明的操作,每个操作用于访问对象结构中一种类型的元素。
    • Element(抽象元素):定义一个accept()方法,该方法通常以一个抽象访问者作为参数。
    • ConcreteElement(具体元素):具体元素实现了accept()方法,在accept()方法中调用访问者的访问方法以便完成对一个元素的操作。
    • ObjectStructure(对象结构):对象结构是一个元素的集合,它用于存放元素对象,并且提供了遍历其内部元素的方法。
  • 补充说明:

    • 访问者模式,包含访问者和被访问元素两个主要组成部分。
    • 这些被访问的元素通常具有不同的类型,且不同的访问者可以对它们进行不同的访问操作。
    • 被访问的元素通常存储在一个集合中,这个集合称为“对象结构”。访问者通过遍历对象结构实现对其中存储的元素的逐个操作。
    • 访问者模式包括两个层次结构:一个是访问者层次结构,提供了抽象访问者和具体访问者;另一个是元素层次结构,提供了抽象元素和具体元素。
  • 优点:

    • 增加新的访问操作很方便。
    • 将有关元素对象的访问行为集中到一个访问者对象中,而不是分散在一个个的元素类中。类的职责更加清晰。
  • 缺点:

    • 增加新的元素类很困难。
    • 破坏封装。

在这里插入图片描述

二、访问者模式示例

使用访问者模式,设计系统中员工数据汇总模块

  • 员工包括正式员工和临时工,每周人力资源部和财务部等部门需要对员工数据进行汇总。人力资源部负责汇总每周员工工作时间,而财务部负责计算每周员工工资。
    • 正式员工每周工作时间为40小时。不同级别、不同部门的员工每周基本工资不同。如果超过40小时,超出部分按照100元/小时作为加班费;如果少于40小时,所缺时间按照请假处理,请假所扣工资以80元/小时计算,直到基本工资扣除到零为止。
    • 临时工每周工作时间不固定。基本工资按小时计算,不同岗位的临时工小时工资不同。
  • FADepartment表示财务部,HRDepartment表示人力资源部,它们充当具体访问者角色,其抽象父类Department充当抽象访问者角色;
  • EmployeeList充当对象结构,用于存储员工列表;
  • FulltimeEmployee表示正式员工,ParttimeEmployee表示临时工,它们充当具体元素角色,其父接口Employee充当抽象元素角色。
"""访问者模式"""### 抽象访问者class Departement:"""部门"""def visit_fulltime_employee(self, employee: "FulltimeEmployee"):raise NotImplementedErrordef visit_parttime_employee(self, employee: "ParttimeEmployee"):raise NotImplementedError### 具体访问者class Finance(Departement):"""财务部"""def visit_fulltime_employee(self, employee):"""实现财务部对全职员工的访问"""if employee.work_time > 40:employee.weekly_wage = (employee.weekly_wage + (employee.work_time - 40) * 100)else:employee.weekly_wage = (employee.weekly_wage - (40 - employee.work_time) * 100)if employee.weekly_wage < 0:employee.weekly_wage = 0print(f"正式员工:{employee.name},实际工资为:{employee.weekly_wage}")def visit_parttime_employee(self, employee):"""实现人事部对兼职员工的访问"""print(f"兼职员工:{employee.name},实际工资为:{employee.work_time*employee.hourly_wage}")class HR(Departement):"""人事部"""def visit_fulltime_employee(self, employee):"""实现人事部对全职员工的访问"""print(f"正式员工:{employee.name},实际工作时间:{employee.work_time}")if employee.work_time > 40:print(f"正式员工:{employee.name},加班时间为:{employee.work_time - 40}")else:print(f"正式员工:{employee.name},请假时间为:{40-employee.work_time}")def visit_parttime_employee(self, employee):"""实现人事部对兼职员工的访问"""print(f"兼职员工:{employee.name},实际工作时间:{employee.work_time}")### 抽象元素class Employee:"""员工"""def accept(self, handler: Departement):"""接受一个抽象访问者访问"""raise NotImplementedError### 具体元素class FulltimeEmployee(Employee):"""全职员工"""def __init__(self, name: str, weekly_wage: float, work_time: int):self.name = name  # 员工姓名self.weekly_wage = weekly_wage  # 员工周薪self.work_time = work_time  # 工作时间def accept(self, handler):handler.visit_fulltime_employee(self)  # 调用访问者的访问方法class ParttimeEmployee(Employee):"""兼职员工"""def __init__(self, name: str, hourly_wage: float, work_time: int):self.name = name  # 员工姓名self.hourly_wage = hourly_wage  # 员工时薪self.work_time = work_time  # 工作时间def accept(self, handler):handler.visit_parttime_employee(self)  # 调用访问者的访问方法### 对象结构class EmployeeList:"""员工列表"""def __init__(self):self.employees = []def add_employee(self, employee):self.employees.append(employee)def accept(self, departement):for employee in self.employees:employee.accept(departement)def __str__(self):return "\n".join([str(employee) for employee in self.employees])
  • 客户端代码
### 客户端代码
if __name__ == "__main__":employee_list = EmployeeList()employee_list.add_employee(FulltimeEmployee("张三", 5000, 45))employee_list.add_employee(FulltimeEmployee("李四", 6000, 40))employee_list.add_employee(FulltimeEmployee("王五", 7000, 38))employee_list.add_employee(ParttimeEmployee("赵六", 80, 20))employee_list.add_employee(ParttimeEmployee("孙七", 60, 18))dep: Departement = Finance()# 如果需要更换具体访问者类,无须修改源代码,只需修改配置文件。# dep: Departement = HR()employee_list.accept(dep)
  • 输出结果
正式员工:张三,实际工资为:5500
正式员工:李四,实际工资为:6000
正式员工:王五,实际工资为:6800
兼职员工:赵六,实际工资为:1600
兼职员工:孙七,实际工资为:1080
  • 当访问者更换为HR时,输出结果
正式员工:张三,实际工作时间:45
正式员工:张三,加班时间为:5
正式员工:李四,实际工作时间:40
正式员工:李四,请假时间为:0
正式员工:王五,实际工作时间:38
正式员工:王五,请假时间为:2
兼职员工:赵六,实际工作时间:20
兼职员工:孙七,实际工作时间:18

您正在阅读的是《设计模式Python版》专栏!关注不迷路~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/73678.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

安全无事故连续天数计算,python 时间工具的高效利用

安全天数计算&#xff0c;数据系统时间直取&#xff0c;安全标准高效便捷好用。 笔记模板由python脚本于2025-03-17 23:50:52创建&#xff0c;本篇笔记适合对python时间工具有研究欲的coder翻阅。 【学习的细节是欢悦的历程】 博客的核心价值&#xff1a;在于输出思考与经验&am…

大型语言模型(LLM)部署中的内存消耗计算

在部署大型语言模型&#xff08;LLM&#xff09;时&#xff0c;显存&#xff08;VRAM&#xff09;的合理规划是决定模型能否高效运行的核心问题。本文将通过详细的公式推导和示例计算&#xff0c;系统解析模型权重、键值缓存&#xff08;KV Cache&#xff09;、激活内存及额外开…

Mysql表的查询

一&#xff1a;创建一个新的数据库&#xff08;companydb),并查看数据库。 二&#xff1a;使用该数据库&#xff0c;并创建表worker。 mysql> use companydb;mysql> CREATE TABLE worker(-> 部门号 INT(11) NOT NULL,-> 职工号 INT(11) NOT NULL,-> 工作时间 D…

ASP.NET Webform和ASP.NET MVC 后台开发 大概80%常用技术

本文涉及ASP.NET Webform和ASP.NET MVC 后台开发大概80%技术 2019年以前对标 深圳22K左右 广州18K左右 武汉16K左右 那么有人问了2019年以后的呢&#xff1f; 答&#xff1a;吉祥三宝。。。 So 想继续看下文的 得有自己的独立判断能力。 C#.NET高级笔试题 架构 优化 性能提…

首页性能优化

首页性能提升是前端优化中的核心任务之一&#xff0c;因为首页是用户访问的第一入口&#xff0c;其加载速度和交互体验直接影响用户的留存率和转化率。 1. 性能瓶颈分析 在优化之前&#xff0c;首先需要通过工具分析首页的性能瓶颈。常用的工具包括&#xff1a; Chrome DevTo…

一周学会Flask3 Python Web开发-SQLAlchemy删除数据操作-班级模块

锋哥原创的Flask3 Python Web开发 Flask3视频教程&#xff1a; 2025版 Flask3 Python web开发 视频教程(无废话版) 玩命更新中~_哔哩哔哩_bilibili 首页list.html里加上删除链接&#xff1a; <!DOCTYPE html> <html lang"en"> <head><meta c…

改变一生的思维模型【12】笛卡尔思维模型

目录 基本结构 警惕认知暗礁 案例分析应用 一、怀疑阶段:破除惯性认知 二、解析阶段:拆解问题为最小单元 三、整合阶段:重构逻辑链条 四、检验阶段:多维验证解决方案 总结与启示 笛卡尔说,唯独自己的思考是可以相信的。 世界上所有的事情,都是值得被怀疑的,但是…

需求文档(PRD,Product Requirement Document)的基本要求和案例参考:功能清单、流程图、原型图、逻辑能力和表达能力

文章目录 引言I 需求文档的基本要求结构清晰内容完整语言准确图文结合版本管理II 需求文档案例参考案例1:电商平台“商品中心”功能需求(简化版)案例2:教育类APP“记忆宝盒”非功能需求**案例3:软件项目的功能需求模板3.1 功能需求III 需求文档撰写技巧1. **从核心逻辑出发…

五大方向全面对比 IoTDB 与 OpenTSDB

对比系列第三弹&#xff0c;详解 IoTDB VS OpenTSDB&#xff01; 之前&#xff0c;我们已经深入探讨了时序数据库 Apache IoTDB 与 InfluxDB、Apache HBase 在架构设计、性能和功能方面等多个维度的区别。还没看过的小伙伴可以点击阅读&#xff1a; Apache IoTDB vs InfluxDB 开…

Electron使用WebAssembly实现CRC-16 MAXIM校验

Electron使用WebAssembly实现CRC-16 MAXIM校验 将C/C语言代码&#xff0c;经由WebAssembly编译为库函数&#xff0c;可以在JS语言环境进行调用。这里介绍在Electron工具环境使用WebAssembly调用CRC-16 MAXIM格式校验的方式。 CRC-16 MAXIM校验函数WebAssembly源文件 C语言实…

vue3vue-elementPlus-admin框架中form组件的upload写法

dialog中write组件代码 let ImageList reactive<UploadFile[]>([])const formSchema reactive<FormSchema[]>([{field: ImageFiles,label: 现场图片,component: Upload,colProps: { span: 24 },componentProps: {limit: 5,action: PATH_URL /upload,headers: {…

Linux mount和SSD分区

为什么要用 mount&#xff1f; Linux 的文件系统结构是单一的树状层次 所有文件、目录和设备都从根目录 / 开始延伸。 外部的存储设备&#xff08;如硬盘、U盘、网络存储&#xff09;或虚拟文件系统&#xff08;如 /proc、/sys&#xff09;必须通过挂载点“嫁接”到这棵树上&a…

【Function】Azure Function通过托管身份或访问令牌连接Azure SQL数据库

【Function】Azure Function通过托管身份或访问令牌连接Azure SQL数据库 推荐超级课程: 本地离线DeepSeek AI方案部署实战教程【完全版】Docker快速入门到精通Kubernetes入门到大师通关课AWS云服务快速入门实战目录 【Function】Azure Function通过托管身份或访问令牌连接Azu…

举例说明 牛顿法 Hessian 矩阵

矩阵求逆的方法及示例 目录 矩阵求逆的方法及示例1. 伴随矩阵法2. 初等行变换法矩阵逆的实际意义1. 求解线性方程组2. 线性变换的逆操作3. 数据分析和机器学习4. 优化问题牛顿法原理解释举例说明 牛顿法 Hessian 矩阵1. 伴随矩阵法 原理:对于一个 n n n 阶方阵 A A

安科瑞分布式光伏监测系统:推动绿色能源高效发展

安科瑞顾强 为应对传统能源污染与资源短缺&#xff0c;分布式光伏发电成为关键解决方案。安科瑞Acrel-1000DP分布式光伏监控系统结合光功率预测技术&#xff0c;有效提升发电稳定性&#xff0c;助力上海汽车变速器有限公司8.3MW屋顶光伏项目实现清洁能源高效利用。 项目亮点 …

从零开始使用 **Taki + Node.js** 实现动态网页转静态网站的完整代码方案

以下是从零开始使用 Taki Node.js 实现动态网页转静态网站的完整代码方案&#xff0c;包含预渲染、自动化构建、静态托管及优化功能&#xff1a; 一、环境准备 1. 初始化项目 mkdir static-site && cd static-site npm init -y2. 安装依赖 npm install taki expre…

商业智能BI分析中,汽车4S销售行业的返厂频次有什么分析价值?

买过车的朋友会发现&#xff0c;同一款车不管在哪个4S店去买&#xff0c;基本上价格都相差不大。即使有些差别&#xff0c;也是带着附加条件的&#xff0c;比如要做些加装需要额外再付一下费用。为什么汽车4S销售行业需要商业智能BI&#xff1f;就是因为在汽车4S销售行业&#…

静态链接过程发生了什么?

在静态链接过程中主要发生了两件事。一是空间与地址分配&#xff0c;链接器扫描所有输入文件的段&#xff0c;合并相似段并且重新计算段长度和在虚拟内存中的映射关系&#xff0c;收集所有的符号放到全局符号表中。二是符号解析与重定位&#xff0c;链接器收集所有的段信息和重…

✎ 一次有趣的经历

&#x1f4c6;2025年3月17日 | 周一 | ☀️晴 &#x1f4cd;今天路过学院楼7&#xff0c;见到了满园盛开的花&#x1f33a;&#xff0c;心情瞬间明朗&#xff01; &#x1f4cc;希望接下来的日子也能像这些花一样&#xff0c;充满活力&#x1f525;&#xff01; &#x1…

docker安装redis

第一步&#xff1a;docker拉取redis镜像 这种命令如果没有指定版本则是最新版本&#xff1a;docker pull redis 成功了 docker images 查询已经拉取成功镜像 然后因为在容器内部我们修改redis的配置不好修改&#xff0c;所以我们可以进行挂载配置文件 这个配置文件可以方便…