设计模式 23 访问者模式

设计模式 23

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

访问者模式(Visitor Pattern)

1 定义

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

2 结构

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

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

UML 类图

+---------------------------+         +---------------------------+
|      Visitor              | <------ |     Element               |
+---------------------------+         +---------------------------+
| + VisitElementA():void    |         | + Accept(v:Visitor): void |
| + VisitElementB():void    |         +---------------------------+
+---------------------------+               ^
        ^                                   |
        |                                   |
+---------------------------+         +---------------------------+
| ConcreteVisitor           |         | ConcreteElement           |
+---------------------------+         +---------------------------+
| + VisitElementA():void    |         | + Accept(v:Visitor): void |
| + VisitElementB():void    |         | + OperationA(): void      |
+---------------------------+         +---------------------------+

3 示例代码

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

访问者接口

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

具体访问者

// 具体访问者 - 报表生成器
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.");
    }
}

元素接口

// 元素接口
public interface IEmployee
{
    void Accept(IVisitor visitor);
}

具体元素类

// 具体元素 - 工程师
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);
    }
}

对象结构

// 对象结构 - 员工列表
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);
        }
    }
}

客户端代码

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);
    }
}

运行结果

Generating report for Engineer: John
Generating report for Manager: Alice with 5 subordinates.

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

4 特点

  • 优点:

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

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

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

  • 缺点:

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

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

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

5 适用场景

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

6 总结

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

  • 18
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

WineMonk

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值