设计模式 23 访问者模式

设计模式 23

  • 创建型模式(5):工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式
  • 结构型模式(7):适配器模式、桥接模式、组合模式、装饰者模式、外观模式、享元模式、代理模式
  • 行为型模式(11):责任链模式、命令模式、解释器模式、迭代器模式、中介者模式、备忘录模式、观察者模式、状态模式、策略模式、模板方法模式、访问者模式

文章目录

  • [设计模式 23](#设计模式 23)
    • [访问者模式(Visitor Pattern)](#访问者模式(Visitor Pattern))
      • [1 定义](#1 定义)
      • [2 结构](#2 结构)
      • [3 示例代码](#3 示例代码)
      • [4 特点](#4 特点)
      • [5 适用场景](#5 适用场景)
      • [6 总结](#6 总结)

访问者模式(Visitor Pattern)

1 定义

访问者模式通过引入一个访问者接口,使得你可以在元素类中接受访问者,并让访问者决定对元素的具体操作。访问者模式的关键在于分离算法和数据结构,使得新的操作可以轻松地添加而不影响已有的数据结构。

2 结构

访问者模式包含以下角色:

  • 访问者接口(Visitor) : 为每种元素类型定义一个访问方法。访问者接口通常提供一个Visit方法,针对不同的元素类有不同的实现。
  • 具体访问者(ConcreteVisitor): 实现访问者接口的具体操作,对元素执行具体的操作。
  • 元素接口(Element) : 声明一个Accept方法,该方法接受访问者对象并调用访问者的Visit方法。
  • 具体元素(ConcreteElement) : 实现元素接口,定义元素的具体行为,并在Accept方法中调用访问者的对应方法。
  • 对象结构(ObjectStructure): 通常是一个包含多个不同类型元素的集合,它可以遍历这些元素并让访问者访问它们。

UML 类图

scss 复制代码
+---------------------------+         +---------------------------+
|      Visitor              | <------ |     Element               |
+---------------------------+         +---------------------------+
| + VisitElementA():void    |         | + Accept(v:Visitor): void |
| + VisitElementB():void    |         +---------------------------+
+---------------------------+               ^
        ^                                   |
        |                                   |
+---------------------------+         +---------------------------+
| ConcreteVisitor           |         | ConcreteElement           |
+---------------------------+         +---------------------------+
| + VisitElementA():void    |         | + Accept(v:Visitor): void |
| + VisitElementB():void    |         | + OperationA(): void      |
+---------------------------+         +---------------------------+

3 示例代码

假设我们要实现一个报表系统,系统中包含不同类型的员工(如工程师和经理),每种员工有不同的报表要求。我们可以使用访问者模式来实现报表的生成,使得报表的生成与员工类型的实现分离。

访问者接口

csharp 复制代码
// 访问者接口
public interface IVisitor
{
    void Visit(Engineer engineer);
    void Visit(Manager manager);
}

具体访问者

csharp 复制代码
// 具体访问者 - 报表生成器
public class ReportGenerator : IVisitor
{
    public void Visit(Engineer engineer)
    {
        Console.WriteLine($"Generating report for Engineer: {engineer.Name}");
    }

    public void Visit(Manager manager)
    {
        Console.WriteLine($"Generating report for Manager: {manager.Name} with {manager.SubordinatesCount} subordinates.");
    }
}

元素接口

csharp 复制代码
// 元素接口
public interface IEmployee
{
    void Accept(IVisitor visitor);
}

具体元素类

csharp 复制代码
// 具体元素 - 工程师
public class Engineer : IEmployee
{
    public string Name { get; private set; }

    public Engineer(string name)
    {
        Name = name;
    }

    public void Accept(IVisitor visitor)
    {
        visitor.Visit(this);
    }
}

// 具体元素 - 经理
public class Manager : IEmployee
{
    public string Name { get; private set; }
    public int SubordinatesCount { get; private set; }

    public Manager(string name, int subordinatesCount)
    {
        Name = name;
        SubordinatesCount = subordinatesCount;
    }

    public void Accept(IVisitor visitor)
    {
        visitor.Visit(this);
    }
}

对象结构

csharp 复制代码
// 对象结构 - 员工列表
public class EmployeeStructure
{
    private List<IEmployee> _employees = new List<IEmployee>();

    public void Attach(IEmployee employee)
    {
        _employees.Add(employee);
    }

    public void Detach(IEmployee employee)
    {
        _employees.Remove(employee);
    }

    public void Accept(IVisitor visitor)
    {
        foreach (var employee in _employees)
        {
            employee.Accept(visitor);
        }
    }
}

客户端代码

csharp 复制代码
class Program
{
    static void Main(string[] args)
    {
        // 创建员工结构
        EmployeeStructure employeeStructure = new EmployeeStructure();

        // 添加员工
        employeeStructure.Attach(new Engineer("John"));
        employeeStructure.Attach(new Manager("Alice", 5));

        // 创建报表生成器
        ReportGenerator reportGenerator = new ReportGenerator();

        // 生成报表
        employeeStructure.Accept(reportGenerator);
    }
}

运行结果

plaintext 复制代码
Generating report for Engineer: John
Generating report for Manager: Alice with 5 subordinates.

在这个例子中,IVisitor 定义了对不同员工类型(EngineerManager)的访问方法。ReportGenerator 是具体的访问者,实现了生成报表的逻辑。IEmployee 接口定义了 Accept 方法,EngineerManager 作为具体的元素,实现了接受访问者的逻辑。EmployeeStructure 作为对象结构,管理了所有的员工,并允许访问者访问这些员工。

4 特点

  • 优点:

    • 增加新的操作容易: 可以在不修改元素类的情况下,通过添加新的访问者类来增加新的操作。

    • 将操作与对象结构分离: 访问者模式将数据结构和操作分离,使得数据结构和操作各自独立,符合单一职责原则。

    • 扩展性好: 可以很容易地增加新的访问者来实现新的功能。

  • 缺点:

    • 增加元素类的复杂性: 每个元素类都必须实现接受访问者的方法,这可能会增加类的复杂性。

    • 违反开闭原则: 如果需要修改元素的结构或添加新的元素类型,则需要修改所有的访问者类,这与开闭原则相违背。

    • 双分派: 访问者模式要求进行双分派,即根据元素类型和访问者类型分别调用相应的方法,这可能会导致系统的复杂性增加。

5 适用场景

  • 对象结构稳定: 当对象结构相对稳定,但操作经常变化时,使用访问者模式可以有效地管理这些变化。
  • 需要对对象结构中的对象进行复杂操作: 访问者模式适用于对对象结构中的元素进行复杂操作,且这些操作可能会频繁变化的场景。
  • 对象结构中包含多个不相关的类: 当对象结构中包含多个不相关的类,需要对这些类执行某些操作时,访问者模式可以通过统一的访问者接口来处理这些操作。

6 总结

访问者模式通过将操作分离到独立的访问者对象中,使得在不修改元素类的情况下,可以增加新的操作。它适用于对象结构稳定但操作经常变化的场景。然而,由于需要对每个元素类增加接受访问者的方法,并且可能导致违反开闭原则,因此在使用时需要权衡利弊。在需要对复杂对象结构进行扩展和管理时,访问者模式是一种强大的设计模式。

相关推荐
Damon_X19 分钟前
桥接模式(Bridge Pattern)
设计模式·桥接模式
越甲八千5 小时前
重温设计模式--享元模式
设计模式·享元模式
码农爱java6 小时前
设计模式--抽象工厂模式【创建型模式】
java·设计模式·面试·抽象工厂模式·原理·23种设计模式·java 设计模式
越甲八千6 小时前
重温设计模式--中介者模式
windows·设计模式·中介者模式
犬余7 小时前
设计模式之桥接模式:抽象与实现之间的分离艺术
笔记·学习·设计模式·桥接模式
Theodore_10228 小时前
1 软件工程——概述
java·开发语言·算法·设计模式·java-ee·软件工程·个人开发
越甲八千9 小时前
重拾设计模式--组合模式
设计模式·组合模式
思忖小下12 小时前
梳理你的思路(从OOP到架构设计)_设计模式Composite模式
设计模式·组合模式·eit
机器视觉知识推荐、就业指导12 小时前
C++设计模式:组合模式(公司架构案例)
c++·后端·设计模式·组合模式
越甲八千13 小时前
重拾设计模式--工厂模式(简单、工厂、抽象)
c++·设计模式