欢迎来到 C# 设计模式 的第十七章!在这一章中,我们将深入探讨 访问者模式(Visitor Pattern)。访问者模式是一种行为型设计模式,它允许你将算法与对象结构分离,使得你可以在不修改对象结构的情况下添加新的操作。想象一下,你正在参观一个艺术展览,每个展品都可以由不同的评论家进行评价,而展品本身不需要知道如何被评价。访问者模式正是这样一种模式,它让你可以通过“访问者”的帮助,轻松地对对象结构中的元素执行各种操作,而不需要修改这些元素的代码。


第一节:什么是访问者模式?

1. 概念

访问者模式 是一种行为型设计模式,它允许你在不修改对象结构的情况下,定义新的操作。通过访问者模式,你可以将算法与对象结构分离,使得你可以在不改变对象结构的前提下,为对象结构中的元素添加新的操作。访问者模式的核心思想是将操作从对象结构中分离出来,交给外部的“访问者”来执行。

幽默小贴士:

访问者模式就像是《哈利·波特》中的魔法评论家,他们可以对每一件展品进行不同的评价,而展品本身不需要知道如何被评价。就像魔法世界中的艺术品一样,它们只需要展示自己,而评论家们会根据自己的标准进行评价! ✨

2. 为什么需要访问者模式?

在现实生活中,我们经常遇到这样的情况:我们需要对一组对象执行不同的操作,而这些操作可能会随着时间的推移而变化。例如:

  • 文件系统操作:你可能需要遍历文件系统中的文件和文件夹,并对它们执行不同的操作,如复制、删除或压缩。每次添加新的操作时,你不希望修改文件和文件夹类的代码。

  • 图形绘制:你可能需要绘制不同类型的图形(如圆形、矩形、三角形等),并且希望为每种图形添加新的绘制方式(如填充颜色、描边等)。每次添加新的绘制方式时,你不希望修改图形类的代码。

  • 代码分析工具:你可能需要编写一个代码分析工具,能够遍历代码中的类、方法、变量等,并对它们执行不同的分析操作(如检查代码风格、检测潜在错误等)。每次添加新的分析操作时,你不希望修改代码结构类的代码。

如果没有访问者模式,每次添加新的操作时,你可能需要修改每个对象类的代码,这不仅会让代码变得复杂,还难以维护。而通过访问者模式,你可以将操作封装到独立的“访问者”类中,使得代码更加简洁、灵活和可扩展。

幽默小贴士:

没有访问者模式的世界就像没有魔法评论家的艺术展览,每件展品都需要自己学会如何被评价。有了访问者模式,你可以像《哈利·波特》中的魔法评论家一样,轻松地对每件展品进行不同的评价,而展品本身只需要展示自己! 🎨


第二节:真实案例

案例 1:文件系统操作

假设你正在开发一个文件管理系统,用户可以选择对文件和文件夹执行不同的操作,如复制、删除或压缩。你可以使用访问者模式来实现这个功能,确保每次添加新的操作时,不需要修改文件和文件夹类的代码。

幽默小贴士:

在文件系统中,访问者模式就像是“魔法操作员”,他们可以对文件和文件夹执行不同的操作,而文件和文件夹本身不需要知道如何被操作。就像《哈利·波特》中的魔法图书馆一样,你可以轻松地对每本书进行不同的处理,而书本身只需要静静地待在书架上! 📚

案例 2:图形绘制

假设你正在开发一个绘图应用程序,用户可以选择绘制不同类型的图形(如圆形、矩形、三角形等),并且希望为每种图形添加新的绘制方式(如填充颜色、描边等)。你可以使用访问者模式来实现这个功能,确保每次添加新的绘制方式时,不需要修改图形类的代码。

幽默小贴士:

在绘图应用程序中,访问者模式就像是“魔法画家”,他们可以为每种图形添加不同的绘制方式,而图形本身不需要知道如何被绘制。就像《爱丽丝梦游仙境》中的神奇画笔一样,你可以轻松地为每个形状添加不同的效果,而形状本身只需要保持原样! 🖌️

案例 3:代码分析工具

假设你正在开发一个代码分析工具,用户可以选择对代码中的类、方法、变量等执行不同的分析操作(如检查代码风格、检测潜在错误等)。你可以使用访问者模式来实现这个功能,确保每次添加新的分析操作时,不需要修改代码结构类的代码。

幽默小贴士:

在代码分析工具中,访问者模式就像是“魔法分析师”,他们可以对代码中的每个元素执行不同的分析操作,而代码元素本身不需要知道如何被分析。就像《哈利·波特》中的魔法书籍一样,你可以轻松地对每本书进行不同的分析,而书本身只需要静静地待在书架上! 📖


第三节:实施方法

要实现访问者模式,通常需要以下几个步骤:

  1. 定义访问者接口:访问者接口负责定义对不同元素的操作方法。每个操作方法对应一个特定的元素类型。
  2. 定义元素接口:元素接口负责定义接受访问者的方法。每个元素类必须实现这个接口,并提供 Accept() 方法,用于接受访问者的访问。
  3. 实现具体的访问者类:具体的访问者类负责实现访问者接口,并提供对不同元素的具体操作逻辑。
  4. 实现具体的元素类:具体的元素类负责实现元素接口,并提供 Accept() 方法,用于将自身传递给访问者。
  5. 客户端代码:客户端代码负责创建访问者对象和元素对象,并调用元素的 Accept() 方法,将访问者传递给元素。

第四节:类之间的关系

为了实现访问者模式,我们需要以下几个类:

1. 所需的类
1.1 IVisitor 接口
// 定义访问者接口
public interface IVisitor
{
    void VisitConcreteElementA(ConcreteElementA element);
    void VisitConcreteElementB(ConcreteElementB element);
}
  • 作用:IVisitor 是所有访问者的公共接口,定义了对不同元素的操作方法。无论是哪种访问者,都必须实现这个接口。
  • 优点:通过接口定义公共行为,可以支持多态性,允许客户端代码动态地选择不同的访问者对象。
1.2 IElement 接口
// 定义元素接口
public interface IElement
{
    void Accept(IVisitor visitor);
}
  • 作用:IElement 是所有元素的公共接口,定义了接受访问者的方法。无论是哪种元素,都必须实现这个接口。
  • 优点:通过接口定义公共行为,可以支持多态性,允许客户端代码动态地选择不同的元素对象。
1.3 ConcreteElementA 类
// 实现具体的元素A类
public class ConcreteElementA : IElement
{
    // 接受访问者
    public void Accept(IVisitor visitor)
    {
        visitor.VisitConcreteElementA(this);
    }

    // 元素A的特定操作
    public void OperationA()
    {
        Console.WriteLine("执行元素A的操作");
    }
}
  • 作用:ConcreteElementA 是实现了 IElement 接口的具体元素类,负责接受访问者的访问。它还提供了 OperationA() 方法,用于执行元素A的特定操作。
  • 优点:通过访问者模式,ConcreteElementA 可以轻松地接受不同访问者的访问,而不需要修改自身的代码。
1.4 ConcreteElementB 类
// 实现具体的元素B类
public class ConcreteElementB : IElement
{
    // 接受访问者
    public void Accept(IVisitor visitor)
    {
        visitor.VisitConcreteElementB(this);
    }

    // 元素B的特定操作
    public void OperationB()
    {
        Console.WriteLine("执行元素B的操作");
    }
}
  • 作用:ConcreteElementB 是实现了 IElement 接口的具体元素类,负责接受访问者的访问。它还提供了 OperationB() 方法,用于执行元素B的特定操作。
  • 优点:通过访问者模式,ConcreteElementB 可以轻松地接受不同访问者的访问,而不需要修改自身的代码。
1.5 ConcreteVisitor1 类
// 实现具体的访问者1类
public class ConcreteVisitor1 : IVisitor
{
    // 访问元素A
    public void VisitConcreteElementA(ConcreteElementA element)
    {
        Console.WriteLine("访问者1访问了元素A");
        element.OperationA();
    }

    // 访问元素B
    public void VisitConcreteElementB(ConcreteElementB element)
    {
        Console.WriteLine("访问者1访问了元素B");
        element.OperationB();
    }
}
  • 作用:ConcreteVisitor1 是实现了 IVisitor 接口的具体访问者类,负责对不同元素执行特定的操作。它提供了 VisitConcreteElementA() 和 VisitConcreteElementB() 方法,分别用于访问元素A和元素B。
  • 优点:通过访问者模式,ConcreteVisitor1 可以轻松地对不同元素执行特定的操作,而不需要修改元素类的代码。
1.6 ConcreteVisitor2 类
// 实现具体的访问者2类
public class ConcreteVisitor2 : IVisitor
{
    // 访问元素A
    public void VisitConcreteElementA(ConcreteElementA element)
    {
        Console.WriteLine("访问者2访问了元素A");
        element.OperationA();
    }

    // 访问元素B
    public void VisitConcreteElementB(ConcreteElementB element)
    {
        Console.WriteLine("访问者2访问了元素B");
        element.OperationB();
    }
}
  • 作用:ConcreteVisitor2 是实现了 IVisitor 接口的具体访问者类,负责对不同元素执行特定的操作。它提供了 VisitConcreteElementA() 和 VisitConcreteElementB() 方法,分别用于访问元素A和元素B。
  • 优点:通过访问者模式,ConcreteVisitor2 可以轻松地对不同元素执行特定的操作,而不需要修改元素类的代码。
1.7 Client 类(客户端代码)
class Program
{
    static void Main(string[] args)
    {
        // 创建元素对象
        IElement elementA = new ConcreteElementA();
        IElement elementB = new ConcreteElementB();

        // 创建访问者对象
        IVisitor visitor1 = new ConcreteVisitor1();
        IVisitor visitor2 = new ConcreteVisitor2();

        // 使用访问者1访问元素
        Console.WriteLine("使用访问者1访问元素:");
        elementA.Accept(visitor1);
        elementB.Accept(visitor1);

        // 使用访问者2访问元素
        Console.WriteLine("使用访问者2访问元素:");
        elementA.Accept(visitor2);
        elementB.Accept(visitor2);
    }
}
  • 作用:Client 是使用访问者模式的客户端代码。它创建了两个元素对象(ConcreteElementA 和 ConcreteElementB),并创建了两个访问者对象(ConcreteVisitor1 和 ConcreteVisitor2)。然后,它调用元素的 Accept() 方法,将访问者传递给元素,从而让访问者对元素执行特定的操作。
  • 优点:通过访问者模式,客户端代码可以在不修改元素类的情况下,轻松地为元素添加新的操作。

第五节:程序执行与输出

让我们运行上面的代码,看看它的输出结果。

输出结果:
使用访问者1访问元素:
访问者1访问了元素A
执行元素A的操作
访问者1访问了元素B
执行元素B的操作
使用访问者2访问元素:
访问者2访问了元素A
执行元素A的操作
访问者2访问了元素B
执行元素B的操作
代码分析:
  1. 创建元素对象:首先,客户端代码创建了两个元素对象(ConcreteElementA 和 ConcreteElementB)。每个元素对象都实现了 IElement 接口,并提供了 Accept() 方法,用于接受访问者的访问。

  2. 创建访问者对象:接下来,客户端代码创建了两个访问者对象(ConcreteVisitor1 和 ConcreteVisitor2)。每个访问者对象都实现了 IVisitor 接口,并提供了对不同元素的具体操作逻辑。

  3. 使用访问者1访问元素:然后,客户端代码调用元素的 Accept() 方法,将 ConcreteVisitor1 传递给元素。ConcreteVisitor1 会对每个元素执行特定的操作,并调用元素的特定方法(如 OperationA() 和 OperationB())。

  4. 使用访问者2访问元素:最后,客户端代码再次调用元素的 Accept() 方法,将 ConcreteVisitor2 传递给元素。ConcreteVisitor2 也会对每个元素执行特定的操作,并调用元素的特定方法。

幽默小贴士:

访问者模式就像是“魔法评论家”,他们可以对每件展品进行不同的评价,而展品本身不需要知道如何被评价。就像《哈利·波特》中的魔法展览一样,你可以轻松地为每个展品添加不同的评价,而展品只需要展示自己! ✨


示例 2:文件系统操作

除了简单的元素和访问者示例,我们还可以将其应用于更复杂的场景,比如文件系统的操作。假设你正在开发一个文件管理系统,用户可以选择对文件和文件夹执行不同的操作,如复制、删除或压缩。你可以使用访问者模式来实现这个功能,确保每次添加新的操作时,不需要修改文件和文件夹类的代码。

1. 所需的类
1.1 IFileVisitor 接口
// 定义文件系统访问者接口
public interface IFileVisitor
{
    void VisitFile(File file);
    void VisitFolder(Folder folder);
}
  • 作用:IFileVisitor 是所有文件系统访问者的公共接口,定义了对文件和文件夹的操作方法。无论是哪种访问者,都必须实现这个接口。
  • 优点:通过接口定义公共行为,可以支持多态性,允许客户端代码动态地选择不同的访问者对象。
1.2 IFileSystemElement 接口
// 定义文件系统元素接口
public interface IFileSystemElement
{
    void Accept(IFileVisitor visitor);
}
  • 作用:IFileSystemElement 是所有文件系统元素的公共接口,定义了接受访问者的方法。无论是文件还是文件夹,都必须实现这个接口。
  • 优点:通过接口定义公共行为,可以支持多态性,允许客户端代码动态地选择不同的文件系统元素。
1.3 File 类
// 实现具体的文件类
public class File : IFileSystemElement
{
    private readonly string _name;

    public File(string name)
    {
        _name = name;
    }

    // 接受访问者
    public void Accept(IFileVisitor visitor)
    {
        visitor.VisitFile(this);
    }

    // 文件的特定操作
    public void PrintInfo()
    {
        Console.WriteLine($"文件名: {_name}");
    }
}
  • 作用:File 是实现了 IFileSystemElement 接口的具体文件类,负责接受访问者的访问。它还提供了 PrintInfo() 方法,用于打印文件的信息。
  • 优点:通过访问者模式,File 可以轻松地接受不同访问者的访问,而不需要修改自身的代码。
1.4 Folder 类
// 实现具体的文件夹类
public class Folder : IFileSystemElement
{
    private readonly string _name;
    private readonly List<IFileSystemElement> _elements = new List<IFileSystemElement>();

    public Folder(string name)
    {
        _name = name;
    }

    // 添加子元素
    public void AddElement(IFileSystemElement element)
    {
        _elements.Add(element);
    }

    // 接受访问者
    public void Accept(IFileVisitor visitor)
    {
        visitor.VisitFolder(this);
        foreach (var element in _elements)
        {
            element.Accept(visitor);
        }
    }

    // 文件夹的特定操作
    public void PrintInfo()
    {
        Console.WriteLine($"文件夹名: {_name}");
    }
}
  • 作用:Folder 是实现了 IFileSystemElement 接口的具体文件夹类,负责接受访问者的访问。它还提供了 AddElement() 方法,用于添加子元素(如文件和文件夹),并提供了 PrintInfo() 方法,用于打印文件夹的信息。
  • 优点:通过访问者模式,Folder 可以轻松地接受不同访问者的访问,而不需要修改自身的代码。
1.5 CopyFileVisitor 类
// 实现具体的复制访问者类
public class CopyFileVisitor : IFileVisitor
{
    // 访问文件
    public void VisitFile(File file)
    {
        Console.WriteLine($"复制文件: {file._name}");
    }

    // 访问文件夹
    public void VisitFolder(Folder folder)
    {
        Console.WriteLine($"复制文件夹: {folder._name}");
    }
}
  • 作用:CopyFileVisitor 是实现了 IFileVisitor 接口的具体访问者类,负责对文件和文件夹执行复制操作。它提供了 VisitFile() 和 VisitFolder() 方法,分别用于访问文件和文件夹,并执行复制操作。
  • 优点:通过访问者模式,CopyFileVisitor 可以轻松地对文件和文件夹执行复制操作,而不需要修改文件和文件夹类的代码。
1.6 DeleteFileVisitor 类
// 实现具体的删除访问者类
public class DeleteFileVisitor : IFileVisitor
{
    // 访问文件
    public void VisitFile(File file)
    {
        Console.WriteLine($"删除文件: {file._name}");
    }

    // 访问文件夹
    public void VisitFolder(Folder folder)
    {
        Console.WriteLine($"删除文件夹: {folder._name}");
    }
}
  • 作用:DeleteFileVisitor 是实现了 IFileVisitor 接口的具体访问者类,负责对文件和文件夹执行删除操作。它提供了 VisitFile() 和 VisitFolder() 方法,分别用于访问文件和文件夹,并执行删除操作。
  • 优点:通过访问者模式,DeleteFileVisitor 可以轻松地对文件和文件夹执行删除操作,而不需要修改文件和文件夹类的代码。
1.7 Client 类(客户端代码)
class Program
{
    static void Main(string[] args)
    {
        // 创建文件和文件夹
        IFileSystemElement file1 = new File("文档1.txt");
        IFileSystemElement file2 = new File("图片1.jpg");
        IFileSystemElement folder = new Folder("我的文件夹");
        folder.AddElement(file1);
        folder.AddElement(file2);

        // 创建访问者对象
        IFileVisitor copyVisitor = new CopyFileVisitor();
        IFileVisitor deleteVisitor = new DeleteFileVisitor();

        // 使用复制访问者访问文件和文件夹
        Console.WriteLine("使用复制访问者访问文件和文件夹:");
        folder.Accept(copyVisitor);

        // 使用删除访问者访问文件和文件夹
        Console.WriteLine("使用删除访问者访问文件和文件夹:");
        folder.Accept(deleteVisitor);
    }
}
  • 作用:Client 是使用访问者模式的客户端代码。它创建了一个文件夹对象,并向其中添加了两个文件对象。然后,它创建了两个访问者对象(CopyFileVisitor 和 DeleteFileVisitor),并调用文件夹的 Accept() 方法,将访问者传递给文件夹及其子元素,从而让访问者对文件和文件夹执行特定的操作。
  • 优点:通过访问者模式,客户端代码可以在不修改文件和文件夹类的情况下,轻松地为文件和文件夹添加新的操作。
输出结果:
使用复制访问者访问文件和文件夹:
复制文件夹: 我的文件夹
复制文件: 文档1.txt
复制文件: 图片1.jpg
使用删除访问者访问文件和文件夹:
删除文件夹: 我的文件夹
删除文件: 文档1.txt
删除文件: 图片1.jpg
代码分析:
  1. 创建文件和文件夹:首先,客户端代码创建了一个文件夹对象,并向其中添加了两个文件对象。每个文件对象和文件夹对象都实现了 IFileSystemElement 接口,并提供了 Accept() 方法,用于接受访问者的访问。

  2. 创建访问者对象:接下来,客户端代码创建了两个访问者对象(CopyFileVisitor 和 DeleteFileVisitor)。每个访问者对象都实现了 IFileVisitor 接口,并提供了对文件和文件夹的具体操作逻辑。

  3. 使用复制访问者访问文件和文件夹:然后,客户端代码调用文件夹的 Accept() 方法,将 CopyFileVisitor 传递给文件夹及其子元素。CopyFileVisitor 会对每个文件和文件夹执行复制操作,并调用文件和文件夹的特定方法(如 PrintInfo())。

  4. 使用删除访问者访问文件和文件夹:最后,客户端代码再次调用文件夹的 Accept() 方法,将 DeleteFileVisitor 传递给文件夹及其子元素。DeleteFileVisitor 也会对每个文件和文件夹执行删除操作,并调用文件和文件夹的特定方法。

幽默小贴士:

文件系统的访问者模式就像是“魔法操作员”,他们可以对文件和文件夹执行不同的操作,而文件和文件夹本身不需要知道如何被操作。就像《哈利·波特》中的魔法图书馆一样,你可以轻松地对每本书进行不同的处理,而书本身只需要静静地待在书架上! 📚


第六节:注意事项

虽然访问者模式非常强大且灵活,但在使用时也有一些需要注意的地方。为了避免潜在的问题,并确保代码的可维护性和性能,以下是几个关键点:

1. 避免过度使用访问者模式
  • 问题:如果对象结构中的元素类型较少,或者操作相对简单,使用访问者模式可能会增加不必要的复杂性。例如,对于只有几个元素的集合,直接在元素类中实现操作可能更简单。

  • 解决方案:在决定是否使用访问者模式时,考虑对象结构的规模和操作的复杂性。如果对象结构较小或操作简单,可以直接在元素类中实现操作,而不需要引入访问者模式。

幽默小贴士:

访问者模式不应该像《爱丽丝梦游仙境》中的疯狂茶会一样,过于复杂和繁琐。确保只在必要时使用访问者模式,就像选择合适的交通工具一样,简单的事情可以用步行解决,复杂的事情再用魔法火车! 🚶‍♂️

2. 处理双分派问题
  • 问题:访问者模式依赖于双分派机制,即通过两次调度来确定具体的操作。第一次调度发生在元素类的 Accept() 方法中,第二次调度发生在访问者类的 Visit() 方法中。这种机制可能会导致代码难以理解,尤其是在对象结构较为复杂的情况下。

  • 解决方案:确保在设计访问者模式时,尽量简化对象结构和操作逻辑,避免过多的层次嵌套。此外,可以在代码中添加注释,帮助其他开发者理解双分派的原理和实现方式。

幽默小贴士:

访问者模式不应该像《哈利·波特》中的魔法决斗一样,因为双分派机制而导致代码难以理解。确保在设计访问者模式时,尽量简化对象结构和操作逻辑,就像魔法师们在决斗时保持冷静一样,避免意外的发生! 🔪

3. 避免频繁修改访问者接口
  • 问题:如果频繁修改访问者接口(如添加新的元素类型或操作方法),可能会导致所有访问者类都需要重新实现。这不仅增加了工作量,还可能导致代码难以维护。

  • 解决方案:在设计访问者模式时,尽量提前规划好对象结构和操作需求,避免频繁修改访问者接口。如果确实需要添加新的元素类型或操作方法,可以考虑使用继承或组合的方式来扩展访问者类,而不是直接修改接口。

幽默小贴士:

访问者模式不应该像《爱丽丝梦游仙境》中的疯狂派对一样,频繁修改访问者接口。确保在设计访问者模式时,尽量提前规划好对象结构和操作需求,就像精心安排的音乐会一样,让每个访问者都有意义! 🎵

4. 处理边界情况
  • 问题:如果某些元素类型没有对应的访问者操作,可能会导致访问者模式的行为不可预测,甚至引发异常。例如,当访问者访问一个未知类型的元素时,可能会抛出 NotImplementedException。

  • 解决方案:在设计访问者模式时,确保为每个元素类型提供默认的访问者操作,或者在访问者类中捕获异常,确保即使遇到未知类型的元素,也不会影响程序的正常运行。

幽默小贴士:

访问者模式不应该像《哈利·波特》中的魔法决斗一样,因为某个元素的变化而导致访问者崩溃。确保在设计访问者模式时,处理好边界情况,就像魔法师们在决斗时保持冷静一样,避免意外的发生! 🔪


本章总结

在这章中,我们学习了 访问者模式 的基本概念、应用场景、实现方法以及注意事项。访问者模式允许你在不修改对象结构的情况下,定义新的操作。它在以下场景中有着重要的应用:

  • 文件系统操作:如遍历文件系统中的文件和文件夹,并对它们执行不同的操作(如复制、删除、压缩等)。
  • 图形绘制:如绘制不同类型的图形(如圆形、矩形、三角形等),并且为每种图形添加新的绘制方式(如填充颜色、描边等)。
  • 代码分析工具:如遍历代码中的类、方法、变量等,并对它们执行不同的分析操作(如检查代码风格、检测潜在错误等)。
关键点回顾:
  • 访问者模式:允许你在不修改对象结构的情况下,定义新的操作。通过访问者模式,你可以将算法与对象结构分离,使得你可以在不改变对象结构的前提下,为对象结构中的元素添加新的操作。
  • 分离关注点:通过访问者模式,你可以将操作从对象结构中分离出来,交给外部的“访问者”来执行,使得代码更加简洁、灵活和可扩展。
  • 简化客户端代码:通过访问者模式,客户端代码可以在不修改元素类的情况下,轻松地为元素添加新的操作。
  • 避免过度使用访问者模式:在决定是否使用访问者模式时,考虑对象结构的规模和操作的复杂性。如果对象结构较小或操作简单,可以直接在元素类中实现操作。
  • 处理双分派问题:访问者模式依赖于双分派机制,确保在设计访问者模式时,尽量简化对象结构和操作逻辑,避免过多的层次嵌套。
  • 避免频繁修改访问者接口:在设计访问者模式时,尽量提前规划好对象结构和操作需求,避免频繁修改访问者接口。如果确实需要添加新的元素类型或操作方法,可以考虑使用继承或组合的方式来扩展访问者类。
  • 处理边界情况:在设计访问者模式时,确保为每个元素类型提供默认的访问者操作,或者在访问者类中捕获异常,确保即使遇到未知类型的元素,也不会影响程序的正常运行。
幽默小贴士:

访问者模式就像是“魔法评论家”,他们可以对每件展品进行不同的评价,而展品本身不需要知道如何被评价。就像《哈利·波特》中的魔法展览一样,你可以轻松地为每个展品添加不同的评价,而展品只需要展示自己! ✨


结束语

亲爱的学员们,恭喜你完成了 C# 设计模式 的第十七章!通过学习访问者模式,你已经掌握了如何通过“访问者”的帮助,轻松地对对象结构中的元素执行各种操作,而不需要修改这些元素的代码。接下来,我们将继续探索更多的设计模式,帮助你编写更加优雅、灵活和可维护的代码。

更多推荐