🎬 HoRain云小助手个人主页

 🔥 个人专栏: 《Linux 系列教程》《c语言教程

⛺️生活的理想,就是为了理想的生活!


⛳️ 推荐

前些天发现了一个超棒的服务器购买网站,性价比超高,大内存超划算!忍不住分享一下给大家。点击跳转到网站。

专栏介绍

专栏名称

专栏介绍

《C语言》

本专栏主要撰写C干货内容和编程技巧,让大家从底层了解C,把更多的知识由抽象到简单通俗易懂。

《网络协议》

本专栏主要是注重从底层来给大家一步步剖析网络协议的奥秘,一起解密网络协议在运行中协议的基本运行机制!

《docker容器精解篇》

全面深入解析 docker 容器,从基础到进阶,涵盖原理、操作、实践案例,助您精通 docker。

《linux系列》

本专栏主要撰写Linux干货内容,从基础到进阶,知识由抽象到简单通俗易懂,帮你从新手小白到扫地僧。

《python 系列》

本专栏着重撰写Python相关的干货内容与编程技巧,助力大家从底层去认识Python,将更多复杂的知识由抽象转化为简单易懂的内容。

《试题库》

本专栏主要是发布一些考试和练习题库(涵盖软考、HCIE、HRCE、CCNA等)

目录

⛳️ 推荐

专栏介绍

核心思想

为什么需要访问者模式

传统方式的局限性

实例

访问者模式的优势

访问者模式的实现

基本结构

实例

使用示例

实例

访问者模式的核心组件

1. Visitor(访问者接口)

实例

2. ConcreteVisitor(具体访问者)

实例

3. Element(元素接口)

实例

4. ConcreteElement(具体元素)

实例

高级应用示例

复杂的文档处理系统

实例

访问者模式的优缺点

优点

缺点

适用场景

适合使用访问者模式的场景

实际应用案例

最佳实践和注意事项

1. 使用双重分派

实例

2. 处理访问者状态

实例

3. 处理访问异常

实例

总结


img

访问者模式是一种行为设计模式,它允许你在不修改已有类结构的情况下,为这些类添加新的操作。简单来说,访问者模式将算法与其所操作的对象结构分离。

核心思想

想象一下你去医院体检的场景:你(被访问者)保持不动,而不同的医生(访问者)会来到你面前进行检查。每个医生都专注于自己的专业领域,但检查的都是同一个你。

在编程中,访问者模式的工作方式类似:

  • 被访问的元素保持稳定不变
  • 访问者可以灵活地添加新的操作
  • 元素接受访问者的访问,让访问者对自己执行操作

为什么需要访问者模式

传统方式的局限性

假设我们有一个图形系统,包含多种形状:

实例

class Circle:
    def __init__(self, radius):
        self.radius = radius
   
    def area(self):
        return 3.14 * self.radius * self.radius
   
    def perimeter(self):
        return 2 * 3.14 * self.radius

class Rectangle:
    def __init__(self, width, height):
        self.width = width
        self.height = height
   
    def area(self):
        return self.width * self.height
   
    def perimeter(self):
        return 2 * (self.width + self.height)

问题来了:如果我们想要添加新的功能,比如计算图形的重心、导出为SVG格式等,就需要修改每个形状类。这违反了开闭原则(对扩展开放,对修改关闭)。

访问者模式的优势

访问者模式通过以下方式解决这个问题:

  • 易于添加新操作:只需创建新的访问者类,无需修改现有元素类
  • 相关操作集中管理:将相关操作组织在同一个访问者中
  • 元素结构稳定:元素类不需要频繁修改

访问者模式的实现

基本结构

让我们通过一个具体的例子来理解访问者模式的实现:

实例

from abc import ABC, abstractmethod

# 1. 定义元素接口
class ShapeElement(ABC):
    @abstractmethod
    def accept(self, visitor):
        pass

# 2. 定义访问者接口
class ShapeVisitor(ABC):
    @abstractmethod
    def visit_circle(self, circle):
        pass
   
    @abstractmethod
    def visit_rectangle(self, rectangle):
        pass

# 3. 具体元素类
class Circle(ShapeElement):
    def __init__(self, radius):
        self.radius = radius
   
    def accept(self, visitor):
        visitor.visit_circle(self)

class Rectangle(ShapeElement):
    def __init__(self, width, height):
        self.width = width
        self.height = height
   
    def accept(self, visitor):
        visitor.visit_rectangle(self)

# 4. 具体访问者类
class AreaCalculator(ShapeVisitor):
    def visit_circle(self, circle):
        area = 3.14 * circle.radius * circle.radius
        print(f"圆的面积: {area:.2f}")
        return area
   
    def visit_rectangle(self, rectangle):
        area = rectangle.width * rectangle.height
        print(f"矩形的面积: {area:.2f}")
        return area

class PerimeterCalculator(ShapeVisitor):
    def visit_circle(self, circle):
        perimeter = 2 * 3.14 * circle.radius
        print(f"圆的周长: {perimeter:.2f}")
        return perimeter
   
    def visit_rectangle(self, rectangle):
        perimeter = 2 * (rectangle.width + rectangle.height)
        print(f"矩形的周长: {perimeter:.2f}")
        return perimeter

使用示例

实例

# 创建图形对象
shapes = [
    Circle(5),
    Rectangle(4, 6),
    Circle(3)
]

# 创建访问者
area_calculator = AreaCalculator()
perimeter_calculator = PerimeterCalculator()

print("=== 计算面积 ===")
for shape in shapes:
    shape.accept(area_calculator)

print("\n=== 计算周长 ===")
for shape in shapes:
    shape.accept(perimeter_calculator)

输出结果:

=== 计算面积 ===
圆的面积: 78.50
矩形的面积: 24.00
圆的面积: 28.26

=== 计算周长 ===
圆的周长: 31.40
矩形的周长: 20.00
圆的周长: 18.84

访问者模式的核心组件

1. Visitor(访问者接口)

定义了对每个具体元素类的访问方法。

实例

class ShapeVisitor(ABC):
    def visit_circle(self, circle): pass
    def visit_rectangle(self, rectangle): pass
    def visit_triangle(self, triangle): pass  # 可以轻松扩展

2. ConcreteVisitor(具体访问者)

实现访问者接口中定义的操作。

实例

class ExportVisitor(ShapeVisitor):
    def visit_circle(self, circle):
        return f'<circle r="{circle.radius}" />'
   
    def visit_rectangle(self, rectangle):
        return f'<rect width="{rectangle.width}" height="{rectangle.height}" />'

3. Element(元素接口)

定义了接受访问者的方法。

实例

class ShapeElement(ABC):
    @abstractmethod
    def accept(self, visitor):
        pass

4. ConcreteElement(具体元素)

实现元素接口,在accept方法中调用访问者的对应方法。

实例

class Circle(ShapeElement):
    def accept(self, visitor):
        visitor.visit_circle(self)  # 这里发生了双重分派


高级应用示例

复杂的文档处理系统

让我们看一个更实际的例子:处理不同类型的文档元素。

实例

from abc import ABC, abstractmethod

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

# 具体文档元素
class Paragraph(DocumentElement):
    def __init__(self, text):
        self.text = text
   
    def accept(self, visitor):
        return visitor.visit_paragraph(self)

class Heading(DocumentElement):
    def __init__(self, text, level):
        self.text = text
        self.level = level
   
    def accept(self, visitor):
        return visitor.visit_heading(self)

class List(DocumentElement):
    def __init__(self, items):
        self.items = items
   
    def accept(self, visitor):
        return visitor.visit_list(self)

# 访问者接口
class DocumentVisitor(ABC):
    @abstractmethod
    def visit_paragraph(self, paragraph): pass
   
    @abstractmethod
    def visit_heading(self, heading): pass
   
    @abstractmethod
    def visit_list(self, list_element): pass

# 具体访问者:HTML导出
class HTMLExportVisitor(DocumentVisitor):
    def visit_paragraph(self, paragraph):
        return f"<p>{paragraph.text}</p>"
   
    def visit_heading(self, heading):
        return f"<h{heading.level}>{heading.text}</h{heading.level}>"
   
    def visit_list(self, list_element):
        items = "".join(f"<li>{item}</li>" for item in list_element.items)
        return f"<ul>{items}</ul>"

# 具体访问者:字数统计
class WordCountVisitor(DocumentVisitor):
    def __init__(self):
        self.total_words = 0
   
    def visit_paragraph(self, paragraph):
        words = len(paragraph.text.split())
        self.total_words += words
        return words
   
    def visit_heading(self, heading):
        words = len(heading.text.split())
        self.total_words += words
        return words
   
    def visit_list(self, list_element):
        words = sum(len(item.split()) for item in list_element.items)
        self.total_words += words
        return words

# 使用示例
document = [
    Heading("Python 教程", 1),
    Paragraph("Python 是一种强大的编程语言。"),
    List(["列表项1", "列表项2", "列表项3"]),
    Paragraph("学习Python很有趣。")
]

# HTML导出
html_visitor = HTMLExportVisitor()
print("=== HTML 导出 ===")
for element in document:
    print(element.accept(html_visitor))

# 字数统计
word_visitor = WordCountVisitor()
print("\n=== 字数统计 ===")
for element in document:
    words = element.accept(word_visitor)
    print(f"{element.__class__.__name__}: {words} 个单词")

print(f"总字数: {word_visitor.total_words}")


访问者模式的优缺点

优点

  1. 开闭原则:容易添加新操作,无需修改现有类
  2. 单一职责原则:将相关行为集中在一个访问者类中
  3. 灵活性:可以在运行时选择不同的访问者
  4. 数据分离:算法与数据结构分离

缺点

  1. 元素接口变更困难:添加新的元素类需要修改所有访问者
  2. 可能破坏封装:访问者可能需要访问元素的私有成员
  3. 复杂性:对于简单的数据结构可能显得过于复杂

适用场景

适合使用访问者模式的场景

  1. 对象结构稳定:需要在一个相对稳定的对象结构上定义多种操作
  2. 操作经常变化:需要频繁添加新的操作或算法
  3. 相关操作集中:希望将相关的操作组织在一起
  4. 复杂对象结构:对象结构包含许多不同类型的对象

实际应用案例

  • 编译器:语法树上的各种分析(类型检查、代码优化等)
  • 文档处理:对文档元素进行不同的处理(导出、统计、格式化等)
  • GUI系统:对UI组件进行不同的操作(渲染、事件处理等)
  • 游戏开发:对游戏对象进行不同的处理(碰撞检测、AI等)

最佳实践和注意事项

1. 使用双重分派

访问者模式的核心是双重分派(double dispatch):

  • 第一次分派:元素接受访问者
  • 第二次分派:访问者访问具体元素

实例

# 在元素中
def accept(self, visitor):
    visitor.visit_circle(self)  # 这里确定了具体的访问方法

2. 处理访问者状态

如果访问者需要维护状态:

实例

class StatisticsVisitor(ShapeVisitor):
    def __init__(self):
        self.total_area = 0
        self.shape_count = 0
   
    def visit_circle(self, circle):
        area = 3.14 * circle.radius * circle.radius
        self.total_area += area
        self.shape_count += 1
   
    def visit_rectangle(self, rectangle):
        area = rectangle.width * rectangle.height
        self.total_area += area
        self.shape_count += 1
   
    def get_statistics(self):
        return {
            'total_area': self.total_area,
            'shape_count': self.shape_count,
            'average_area': self.total_area / self.shape_count if self.shape_count > 0 else 0
        }

3. 处理访问异常

实例

class SafeVisitor(ShapeVisitor):
    def visit_circle(self, circle):
        try:
            # 执行操作
            pass
        except Exception as e:
            print(f"处理圆形时出错: {e}")
   
    def visit_rectangle(self, rectangle):
        try:
            # 执行操作
            pass
        except Exception as e:
            print(f"处理矩形时出错: {e}")


总结

访问者模式是一个强大的设计模式,它通过将算法与数据结构分离,提供了很好的扩展性。虽然它有一定的复杂性,但在合适的场景下能够显著提高代码的维护性和扩展性。

关键要点:

  • 访问者模式适用于对象结构稳定但操作频繁变化的场景
  • 通过双重分派机制实现多态行为
  • 易于添加新操作,但难以添加新元素类型
  • 在实际项目中要权衡其复杂性和带来的好处

❤️❤️❤️本人水平有限,如有纰漏,欢迎各位大佬评论批评指正!😄😄😄

💘💘💘如果觉得这篇文对你有帮助的话,也请给个点赞、收藏下吧,非常感谢!👍 👍 👍

🔥🔥🔥Stay Hungry Stay Foolish 道阻且长,行则将至,让我们一起加油吧!🌙🌙🌙

更多推荐