Python 如何实现访问者设计模式?什么是访问者(Visitor)模式?实际案例中有什么作用?

什么是访问者设计模式?访问者(Visitor)设计模式介绍:

访问者(Visitor)设计模式是一种行为设计模式,用于在不修改被访问对象的前提下定义新的操作。它通过将操作封装到独立的访问者类中,实现了将数据结构操作解耦的目标。这种模式适用于对复杂对象结构进行操作的场景,特别是当对象结构包含多个类型的对象,并且这些对象类型可能会发生变化。

在这里插入图片描述

主要角色:

  1. 访问者接口(Visitor): 定义了访问者类的接口,声明了可以访问哪些元素。

  2. 具体访问者类(ConcreteVisitor): 实现了访问者接口,提供了对每个元素进行操作的具体实现。

  3. 元素接口(Element): 定义了对象结构中的元素的接口,声明了接受访问者的方法。

  4. 具体元素类(ConcreteElement): 实现了元素接口,提供了接受访问者的具体实现。

  5. 对象结构类(ObjectStructure): 包含了多个元素,提供了一个接受访问者的方法,使访问者能够访问结构中的所有元素。

工作流程:

  1. 定义访问者接口,声明访问每种元素的方法。

  2. 实现具体访问者类,为每种元素提供具体的访问操作。

  3. 定义元素接口,声明接受访问者的方法。

  4. 实现具体元素类,提供接受访问者的具体实现。

  5. 定义对象结构类,包含多个元素,提供接受访问者的方法。

  6. 客户端创建访问者对象和对象结构对象,并通过对象结构的接受方法调用访问者的操作。

优点:

  • 分离关注点: 将数据结构与操作解耦,使得可以独立扩展两者之一而不影响另一方。

  • 新增操作方便: 可以轻松地新增对元素的操作,而无需修改元素本身。

  • 适用于复杂结构: 对于包含多个类型对象的复杂结构,访问者模式能更好地组织和管理操作。

Python 实现访问者模式示例代码(一):

from abc import ABC, abstractmethod

# 访问者接口
class Visitor(ABC):
    @abstractmethod
    def visit_element_a(self, element_a):
        pass

    @abstractmethod
    def visit_element_b(self, element_b):
        pass

# 具体访问者类
class ConcreteVisitor(Visitor):
    def visit_element_a(self, element_a):
        print(f"Visiting Element A: {element_a.operation_a()}")

    def visit_element_b(self, element_b):
        print(f"Visiting Element B: {element_b.operation_b()}")

# 元素接口
class Element(ABC):
    @abstractmethod
    def accept(self, visitor):
        pass

# 具体元素类
class ConcreteElementA(Element):
    def accept(self, visitor):
        visitor.visit_element_a(self)

    def operation_a(self):
        return "Operation A"

class ConcreteElementB(Element):
    def accept(self, visitor):
        visitor.visit_element_b(self)

    def operation_b(self):
        return "Operation B"

# 对象结构类
class ObjectStructure:
    def __init__(self):
        self.elements = []

    def add_element(self, element):
        self.elements.append(element)

    def accept(self, visitor):
        for element in self.elements:
            element.accept(visitor)

# 客户端
visitor = ConcreteVisitor()
element_a = ConcreteElementA()
element_b = ConcreteElementB()

object_structure = ObjectStructure()
object_structure.add_element(element_a)
object_structure.add_element(element_b)

object_structure.accept(visitor)

这个示例演示了访问者设计模式在 Python 中的应用。通过定义访问者接口、具体访问者类、元素接口、具体元素类和对象结构类,实现了对元素的访问操作。


Python 实现访问者模式示例代码(二)

假设我们有一个电商网站,有不同类型的商品,例如电子产品、服装和图书。我们希望实现一个价格计算器,根据不同类型的商品和用户等级计算最终价格。这个场景可以使用访问者设计模式。

from abc import ABC, abstractmethod

# 访问者接口
class PriceCalculatorVisitor(ABC):
    @abstractmethod
    def visit_electronic_product(self, electronic_product):
        pass

    @abstractmethod
    def visit_clothing(self, clothing):
        pass

    @abstractmethod
    def visit_book(self, book):
        pass

# 具体访问者类
class StandardPriceCalculator(PriceCalculatorVisitor):
    def visit_electronic_product(self, electronic_product):
        return electronic_product.base_price

    def visit_clothing(self, clothing):
        return clothing.base_price * 0.9  # 服装打九折

    def visit_book(self, book):
        return book.base_price * 0.8  # 图书打八折

# 元素接口
class Product(ABC):
    @abstractmethod
    def accept(self, visitor):
        pass

# 具体元素类
class ElectronicProduct(Product):
    def __init__(self, base_price):
        self.base_price = base_price

    def accept(self, visitor):
        return visitor.visit_electronic_product(self)

class Clothing(Product):
    def __init__(self, base_price):
        self.base_price = base_price

    def accept(self, visitor):
        return visitor.visit_clothing(self)

class Book(Product):
    def __init__(self, base_price):
        self.base_price = base_price

    def accept(self, visitor):
        return visitor.visit_book(self)

# 对象结构类
class ShoppingCart:
    def __init__(self):
        self.products = []

    def add_product(self, product):
        self.products.append(product)

    def calculate_total_price(self, visitor):
        total_price = 0
        for product in self.products:
            total_price += product.accept(visitor)
        return total_price

# 客户端
electronic_product = ElectronicProduct(base_price=100)
clothing = Clothing(base_price=50)
book = Book(base_price=30)

shopping_cart = ShoppingCart()
shopping_cart.add_product(electronic_product)
shopping_cart.add_product(clothing)
shopping_cart.add_product(book)

standard_price_calculator = StandardPriceCalculator()
total_price = shopping_cart.calculate_total_price(standard_price_calculator)

print(f"Total Price: ${total_price}")

在这个示例中,PriceCalculatorVisitor 是访问者接口,定义了不同类型商品的价格计算方法。StandardPriceCalculator 是具体访问者类,实现了实际的价格计算逻辑。Product 是元素接口,定义了接受访问者的方法。ElectronicProductClothingBook 是具体元素类,分别代表不同类型的商品。ShoppingCart 是对象结构类,包含了多个商品,提供了计算总价格的方法。

在客户端,我们创建了一些商品并放入购物车,然后使用访问者模式计算总价格。这个例子展示了访问者设计模式在实际开发中的应用,如何有效地对不同类型的对象进行操作。


使用访问者模式时,需要注意哪些问题?

使用访问者设计模式时,需要注意一些问题,以确保模式的有效实施和代码的可维护性:

  1. 新元素的添加: 如果系统中新增了新的元素类型,需要修改所有的具体访问者类,为新增元素类型添加相应的访问方法。这可能导致修改多个类,违反了开闭原则。

  2. 元素接口修改: 如果元素接口发生变化,所有的具体元素类都需要进行相应的修改。这可能导致修改多个类,破坏了系统的稳定性。

  3. 违反封装: 访问者模式在一定程度上打破了元素的封装性,因为具体访问者类需要访问元素的内部状态。这可能使得元素的内部结构对外部可见,降低了封装性。

  4. 复杂性: 访问者模式引入了多个接口和类,增加了系统的复杂性。对于简单的对象结构,使用访问者模式可能过于繁琐。

  5. 理解困难: 对于不熟悉访问者模式的开发人员,理解其运作机制可能需要一些时间。在团队中使用该模式时,确保团队成员对其有足够的了解是重要的。

  6. 性能开销: 访问者模式可能引入一定的性能开销,特别是当对象结构较大且变动频繁时。在性能敏感的应用中,需要仔细评估使用访问者模式的代价。

  7. 使用场景限制: 访问者模式更适合于对象结构相对稳定且具有多种不同类型元素的情况。如果对象结构变动频繁,或者元素类型较少,可能没有必要引入访问者模式。

虽然访问者模式有一些潜在的问题,但在某些场景下,它仍然是一个强大的设计模式,能够有效地处理复杂的对象结构。在使用时,需要仔细考虑系统的特定需求和约束。


本文就到这里了,感谢您的阅读 。别忘了点赞、收藏~ Thanks♪(・ω・)ノ 🍇

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

JumpServer管理虚拟机

环境准备 1.虚拟机192.168.1.111在线安装JumpServer https://blog.csdn.net/tongxin_tongmeng/article/details/1340166222.虚拟机192.168.1.112创建用户changwq、wangwj useradd changwq && passwd changwq、useradd wangwj && passwd wangwj3.虚拟机192.168.…

springboot容器

1.主要指的是servlet容器 servlet组件由sevlet Filter Listener等 2.自动配置原理 通过ServletWebServerFactoryAutoConfiguration 配置这些内容 (自动配置类开始分析功能) conditionalOnclass开启条件 ServletRequest类 import导入嵌入式的tomcat Jetty等 这些是配置类&…

本地化工具:Soluling Localization Crack

Soluling 是一个本地化工具,包含本地化项目所需的所有功能。Solling 使本地化变得非常容易。Soluling 是桌面应用程序和命令行工具的组合 。Solling支持100多种文件格式。通过 Soluling,您可以本地化桌面应用程序、移动应用程序、Web 应用程序、文档和在…

如何在thingsboard的规则链中对一个遥测属性进行求平均值

背景 有这样一个需求,一个温度传感器每5秒,上传一次数据。要求算出该设备2分钟内的平均温度,如果超过某个值,则发送告警邮件。 具体操作实现 下面在规则链中实现求平均值。 使用的节点是 配置如下 必填 Timeseries keys,是要求的平均值的属性名。 我这里求的是四个…

【教学类-17-03】20231105《世界杯随机参考图七巧板 3份一页》(大班)

效果展示: 单页效果 多页效果 预设样式: 背景需求: 2022年11月24日,大1班随机抽取的9位幼儿制作了9张拼图,发现以下三个问题: 1、粉红色辅助纸选择量多——9份作业有4位幼儿的七巧板人物是粉红色的 2、…

嵌入式杂记 -- MCU的大小端模式

MCU的大小端模式 大端模式小端模式大小端模式测试联合体概念MCU大小端模式测试大端模式测试小端模式测试 大小端模式转换 在进行MCU开发的时候,我们需要注意MCU的数据存储模式,在嵌入式中有两种不同的存储模式,分别是 大端模式和小端模式。 …

dameng数据库数据id decimal类型,精度丢失

问题处理 这一次也是精度丢失,但是问题呢还是不一样,这一次所有的id都被加一了,只有id字段被加一,还有的查询查出来封装成对象之后对象的id字段被减一了,数据库id字段使用的decimal(20,6)&…

Apache Airflow (六) :DAG catchup 参数设置

🏡 个人主页:IT贫道_大数据OLAP体系技术栈,Apache Doris,Clickhouse 技术-CSDN博客 🚩 私聊博主:加入大数据技术讨论群聊,获取更多大数据资料。 🔔 博主个人B栈地址:豹哥教你大数据的个人空间-豹…

软件测试 —— 常见的自动化测试架构!

一个自动化测试架构就是一个集成体系,其中定义了一个特殊软件产品的自动化测试规则。这一体系中包含测试功能函数库、测试数据源、测试对象识别标准,以及各种可重用的模块。这些组件作为小的构建模块,被组合起来代表某种商业流程。自动化测试…

求最大公约数math.gcd()

【小白从小学Python、C、Java】 【计算机等级考试500强双证书】 【Python-数据分析】 求最大公约数 math.gcd() [太阳]选择题 下列代码执行输出的结果是? import math print("【执行】print(math.gcd(6, 8))") print(math.gcd(6, 8)) print(&quo…

【汇编】汇编语言的介绍

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言一、汇编是什么?二、为什么要学习汇编语言?三、学习汇编语言的好处四、安装汇编环境4.1 下载虚拟环境4.2 配置虚拟环境 总结 前言 计算…

我的创作纪念日-我在csdn的三周年

文章目录 机缘收获日常成就憧憬 机缘 2020 年 11 月 09 日,撰写了第 1 篇技术博客,到现在不知不觉三周年了。 慢慢的也会将自己的感受和知识梳理成专栏,记录日常的学习以及通过文章进行技术交流,和大家分享一个我认为比较好的成…

Unity Hub无法登陆的两种终极解决办法

最近换了个电脑,需要重装Unity, 然后unity hub 怎么都无法登陆,登陆不了就不能激活personal license。试了很多次,包括unity hub 2.5.8 和unity hub 3.3都不行,真的是很崩溃。因为是公司的电脑,限制比较多&…

西门子精智屏数据记录U盘插拔问题总结

西门子精智屏数据记录U盘插拔问题总结 注意: 数据记录过程中不允许带电插拔 U 盘! 数据记录的相关功能可参考以下链接中的内容: TIA博途wincc V16 如何进行变量周期归档?

AI大模型低成本快速定制秘诀:RAG和向量数据库

文章目录 1. 前言2. RAG和向量数据库3. 论坛日程4. 购票方式 1. 前言 当今人工智能领域,最受关注的毋庸置疑是大模型。然而,高昂的训练成本、漫长的训练时间等都成为了制约大多数企业入局大模型的关键瓶颈。 这种背景下,向量数据库凭借其独特…

算法笔记-第七章-栈的应用(未完成)

算法笔记-第七章-栈的应用 栈的基本常识栈的解释一栈的解释二 栈的操作序列合法的出栈序列可能的出栈序列补充知识点 后缀表达式(无优先级) 栈的基本常识 栈(Stack)是只允许在一端进行插入或删除操作的线性表。 栈的解释一 栈的…

基础大模型的结构特性与发展

摘要: 基础大模型的结构特性是什么给予的?在建模部分,我们将探索基础模型背后的底层架构,并确定5个关键属性。 首先,我们从讨论计算模型的表现力开始-捕获和吸收真实世界的信息,以及可扩展性-熟练地处理大量…

类和对象(4):Date类.运算符重载 1

一、赋值运算符重载 1.1 运算符重载 运算符重载是具有特殊函数名的函数,函数名字为:关键词operator需要重载的运算符符号。 不能重载C/C中未出现的符号,如:operator。重载操作符必须有一个类类型参数。不能改变用于内置类型运算…

【stack题解】逆波兰表达式求值 | 用队列实现栈

逆波兰表达式求值 力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台 给你一个字符串数组 tokens ,表示一个根据 逆波兰表示法 表示的算术表达式。 请你计算该表达式。返回一个表示表达式值的整数。 注意: 有效的算符为 、-、…

《深入浅出进阶篇》——空间换时间优化——P2671 求和

链接:https://www.luogu.com.cn/problem/P2671 上题干: 题目描述 一条狭长的纸带被均匀划分出了n个格子,格子编号从11到n。每个格子上都染了一种颜色colori​用[1,m]当中的一个整数表示),并且写了一个数字numberi​。…