【愚公系列】2023年10月 二十三种设计模式(二十三)-访问者模式(Vistor Pattern)
🏆 作者简介,愚公搬代码
🏆《头衔》:华为云特约编辑,华为云云享专家,华为开发者专家,华为产品云测专家,CSDN博客专家,阿里云专家博主,腾讯云优秀博主,掘金优秀博主,51CTO博客专家等。
🏆《近期荣誉》:2022年CSDN博客之星TOP2,2022年华为云十佳博主等。
🏆《博客内容》:.NET、Java、Python、Go、Node、前端、IOS、Android、鸿蒙、Linux、物联网、网络安全、大数据、人工智能、U3D游戏、小程序等相关领域知识。
🏆🎉欢迎 👍点赞✍评论⭐收藏
🚀前言
设计模式(Design Pattern)是软件开发领域的宝贵经验,是多人反复借鉴和广泛应用的代码设计指导。它们是一系列经过分类和归纳的代码组织方法,旨在实现可重用性、可维护性和可理解性。使用设计模式,我们能够编写高质量的代码,使其更易于他人理解,并提供了代码可靠性的保证。
毫无疑问,设计模式对个人、团队和整个系统都带来了显著的益处。它们将代码开发提升到工程化水平,为软件工程构建提供了坚实的基础,就如同大厦的一块块精巧的砖石一样。在项目中明智地应用设计模式可以完美地解决各种复杂问题。每种设计模式都有相应的原理和最佳实践,它们描述了我们日常开发中不断遇到的问题,以及这些问题的核心解决方法。正是因为这种实用性和通用性,设计模式才能在软件开发中广泛地得以应用。设计模式是构建稳健、可扩展和可维护软件的关键工具,为开发者们提供了解决问题的智慧和指导。
🚀一、访问者模式(Vistor Pattern)
访问者模式属于行为型设计模式,它允许你定义对某个对象结构中各元素的操作,而无需改变这些元素类的代码。要使用访问者模式,首先需要一个对象结构,该结构具有能够遍历自身各个对象的方法。这个模式的核心概念是将操作与对象的结构分离,使得新增操作变得更容易,同时不会影响到已有的对象类。
🚀二、使用步骤
🔎1.角色
🦋1.1 抽象访问者(Visitor)
访问者模式(Visitor Pattern)是一种行为型设计模式,其主要作用是将数据结构与对数据的操作分离开来。在访问者模式中,抽象访问者(Visitor)是其中一个关键概念,它的作用如下:
封装操作:抽象访问者封装了对数据结构中各元素的具体操作。这些操作可以是不同的,例如,计算、打印、验证等。通过将操作封装在访问者中,可以使操作与数据结构解耦,从而支持新的操作的添加而无需修改数据结构。
提供统一接口:抽象访问者定义了一组抽象的操作方法,这些方法用于访问数据结构中的不同元素。这种统一的接口使得不同的具体访问者可以轻松应用于同一数据结构,而无需修改数据结构或元素类。
支持多态:抽象访问者利用多态机制,让不同的具体访问者能够以不同的方式处理数据结构中的元素。这增加了灵活性,可以根据需要选择合适的访问者。
易于扩展:当需要添加新的操作或处理逻辑时,可以创建新的具体访问者类,而无需修改已有的数据结构或元素类。这符合开放-封闭原则,使系统更容易扩展和维护。
分离关注点:抽象访问者模式将数据结构的遍历与具体操作分离,使得每个部分都可以专注于自己的任务,提高了代码的可维护性和可读性。
抽象访问者在访问者模式中的作用是提供一个统一的接口,封装对数据结构中元素的操作,支持多态和易于扩展。它使得可以轻松地引入新的操作,同时
🦋1.2 具体访问者(Concrete Visitor)
访问者模式(Visitor Pattern)中具体访问者(Concrete Visitor)是实现抽象访问者接口的具体类。具体访问者的主要作用是定义对数据结构中各个元素的具体操作,以实现访问者模式的核心功能。以下是具体访问者的概念和作用:
实现操作:具体访问者类实现了抽象访问者接口中定义的一组操作方法。这些操作方法对应于访问数据结构中的不同元素,每个方法描述了如何处理特定类型的元素。
封装操作逻辑:具体访问者封装了访问数据结构中元素的具体操作逻辑。这可以包括计算、打印、验证等各种操作。每个具体访问者类负责一个或多个相关的操作。
支持多态:具体访问者通过多态机制,使不同的具体访问者能够以不同的方式处理相同的元素。这增加了灵活性,可以根据需要选择合适的具体访问者来执行操作。
可扩展性:当需要添加新的操作或处理逻辑时,可以创建新的具体访问者类,而无需修改已有的数据结构或元素类。这符合开放-封闭原则,使系统更容易扩展和维护。
分离关注点:具体访问者类将数据结构的遍历与具体操作分离开来,使得每个具体访问者可以专注于自己的任务,提高了代码的可维护性和可读性。
应用于数据结构:具体访问者通常用于遍历和操作特定的数据结构,例如,访问者模式可以用于遍历抽象语法树、文档对象模型(DOM)等。
具体访问者是访问者模式的关键组成部分,它负责实现对数据结构中元素的具体操作。
🦋1.3 抽象元素(Element)
访问者模式(Visitor Pattern)中的抽象元素(Element)是表示数据结构中各个元素的接口或抽象类。抽象元素的作用如下:
定义接口:抽象元素定义了一组接口或方法,用于表示数据结构中不同元素的共同特征。这些接口通常包括接受访问者的方法(通常称为
accept
方法),以及可能需要访问者操作的其他方法。使数据结构通用:抽象元素的存在使得数据结构中的不同元素可以具有相同的接口,从而提供了一种通用的方式来处理这些元素。这有助于解耦数据结构与具体操作之间的关系。
支持多态:抽象元素为不同类型的元素提供了一个共同的基类,使得具体元素类可以实现这个基类的接口方法,从而支持多态。这意味着访问者可以以一致的方式访问不同类型的元素。
关注元素本身:抽象元素将元素本身的操作与数据结构的操作分离开来。元素类可以专注于自己的功能,而不需要担心如何与访问者进行交互。
可扩展性:如果需要在数据结构中添加新类型的元素,只需创建一个新的具体元素类,实现抽象元素的接口方法即可,无需修改已有的访问者或数据结构。
应用于数据结构:抽象元素通常用于表示数据结构中的各个元素,例如,抽象语法树中的各种节点、文档对象模型(DOM)中的各种元素等。
抽象元素在访问者模式中扮演着关键角色,它定义了数据结构中各个元素的共同接口,使得数据结构与具体操作之间的关系更加松散,同时支持多态和可扩展性。这有助于将元素的操作与数据结构的遍历分离开来,提高了代码的可维护性。
🦋1.4 具体元素(Concrete Element )
访问者模式(Visitor Pattern)中的具体元素(Concrete Element)是抽象元素(Element)的实现或子类。具体元素代表了数据结构中的某个具体类型的元素。以下是具体元素的概念和作用:
实现抽象元素接口:具体元素实现了抽象元素定义的接口,特别是“接受访问者”的方法(通常叫
accept
)。这使得具体元素能够与访问者进行交互。代表数据结构中的特定元素:具体元素代表了数据结构中的特定实例或类型的元素。例如,如果你在处理一个文档结构,具体元素可以是“段落”、“图片”、“标题”等。
封装元素的数据和行为:除了接受访问者的行为外,具体元素也封装了与该元素相关的数据和行为。这使得每个具体元素可以独立于其他元素和访问者进行操作。
交互与访问者:当具体元素的
accept
方法被调用时,它通常会将自己传递给访问者的访问方法,从而允许访问者对自己进行操作。这是多态机制在访问者模式中的应用。支持扩展:当需要添加新类型的元素时,只需创建一个新的具体元素类并实现抽象元素的接口。这使得添加新元素变得简单,并且不会影响现有的访问者和其他元素。
具体元素在访问者模式中代表了数据结构中的某个具体类型的元素,并实现了与访问者进行交互的接口。它封装了元素的数据和行为,允许访问者对其进行操作,同时支持系统的可扩展性。
🦋1.5 结构对象(Object Structure)
访问者模式(Visitor Pattern)中的结构对象(Object Structure)是一个重要的组成部分,它用于维护一组元素,并提供一种统一的方式让访问者(Visitor)访问这些元素。以下是结构对象的概念和作用:
维护元素集合:结构对象负责维护一组元素,这些元素可以是不同类型的对象,但它们都实现了相同的抽象元素(Element)接口。这些元素可以是组成一个数据结构的各个部分,如树节点、DOM元素等。
提供接受访问者的方法:结构对象通常会提供一种方式,让访问者能够访问其中的元素。这通常是一个
accept
方法,它接受一个访问者作为参数,并将自身传递给访问者。支持遍历元素:结构对象可以提供一种遍历元素的方式,以便访问者能够依次访问其中的元素。这可以是通过迭代器、递归遍历或其他方式来实现。
解耦元素与具体操作:结构对象的存在有助于将元素与具体操作(由具体访问者实现)解耦。元素只需要提供一种通用的接受访问者的方法,而不需要知道具体操作的细节。
支持多个访问者:一个结构对象可以被多个不同的访问者访问,每个访问者可以执行不同的操作。这增加了灵活性,使得可以在不修改元素类的情况下添加新的操作。
易于扩展:当需要向数据结构中添加新的元素类型时,只需扩展结构对象以包含这些新元素,而无需修改现有的访问者或元素类。
结构对象在访问者模式中的作用是维护一组元素,并提供一种通用的方式,让访问者能够访问这些元素,从而实现了元素与具体操作的解耦,同时支持多态和可扩展性。这使得访问者模式成为一种有效的方式来处理复杂的数据结构和多种操作需求。
🔎2.示例
命名空间VistorPattern中包含Student学生基类充当抽象元素,它的3个实现类Kris、Cherry和Harling分别是我的儿子、女儿和侄女的英文名,它们充当具体元素角色。ITeacher接口充当抽象访问者,而Teacher类充当具体访问者。花名册类Roster充当结构对象。本案例尝试使用老师根据花名册来进行家访这样的场景来讲述访问者模式的使用方法。
public abstract class Student {
public string Name { get; protected set; }
public Student(string name) {
Name = name;
}
public abstract void Accept(ITeacher teacher);
}
学生基类Student,代表抽象元素。
public class Kris : Student {
public Kris(string name) : base(name) {
}
public override void Accept(ITeacher teacher) {
teacher.Visit(this);
}
}
Kris类,代表某一具体学生。
public class Cherry : Student {
public Cherry(string name) : base(name) {
}
public override void Accept(ITeacher teacher) {
teacher.Visit(this);
}
}
Cherry类,代表某一具体学生。
public class Harling : Student {
public Harling(string name) : base(name) {
}
public override void Accept(ITeacher teacher) {
teacher.Visit(this);
}
}
Harling类,代表某一具体学生。
public interface ITeacher {
void Visit(Student student);
}
ITeacher接口,代表抽象访问者。
public class Teacher : ITeacher {
private string _name = null;
public Teacher(string name) {
_name = name;
}
public void Visit(Student student) {
Console.WriteLine($"Mr.{_name} is going to visit {student.Name}'s home. ");
}
}
Teacher类,代表具体访问者。
public class Roster {
public List<Student> Students { get; private set; }
public Roster() {
Students = new List<Student> {
new Cherry("Cherry"),
new Kris("Kris"),
new Harling("Harling")
};
}
}
Roster类,代表花名册,充当结构对象,老师根据这个对象来进行家访。
public class Program {
public static void Main(string[] args) {
Roster roster = new Roster();
foreach(var student in roster.Students) {
student.Accept(new Teacher("Tony"));
}
Console.Read();
}
}
以上是调用方的代码,以下是这个案例的输出结果:
Mr.Tony is going to visit Cherry's home.
Mr.Tony is going to visit Kris's home.
Mr.Tony is going to visit Harling's home.
🚀总结
🔎1.优点
访问者模式(Visitor Pattern)具有多个优点,它是一种行为型设计模式,用于处理复杂对象结构和多种操作需求。以下是访问者模式的一些主要优点:
解耦元素和操作:访问者模式将元素的数据结构与具体操作(访问者)分离开来。这意味着您可以添加新的操作而无需修改现有的元素类,以及可以添加新的元素类型而无需修改现有的访问者类。这遵循了开放-封闭原则,增强了系统的可扩展性。
增加新操作容易:在需要添加新的操作时,只需创建一个新的具体访问者类,实现所需的操作即可。这使得在不影响现有代码的情况下扩展系统变得非常简单。
支持多态:访问者模式利用多态性,允许不同类型的元素以不同的方式响应相同的操作。这有助于处理各种元素类型的复杂行为。
封装操作逻辑:每个具体访问者类封装了特定操作的逻辑,使得这些逻辑更易于维护和管理。这提高了代码的可读性和可维护性。
统一操作接口:访问者模式为不同类型的元素提供了一个统一的操作接口,使得可以轻松地在所有元素上执行相同的操作。这有助于统一管理和处理元素。
分离关注点:访问者模式将数据结构遍历和具体操作分离,使每个部分都可以专注于自己的任务。这有助于降低代码的复杂度。
适用于复杂对象结构:访问者模式特别适用于处理包含多层嵌套和复杂结构的对象,例如抽象语法树、文档对象模型(DOM)等。
访问者模式通过解耦元素和操作、支持多态、提供统一操作接口等方式,增强了代码的灵活性、可扩展性和可维护性,特别适用于处理复杂对象结构和多种操作需求的情况。然而,它也可能会引入较多的类和接口,因此在应用时需要权衡其复杂性。
🔎2.缺点
访问者模式(Visitor Pattern)虽然具有许多优点,但也存在一些缺点和限制,需要在使用时进行考虑和权衡:
增加类和接口数量:引入访问者模式通常会导致增加许多具体访问者和具体元素类,尤其是在处理多种操作和元素类型的复杂系统中。这可能增加代码的复杂性,使代码库变得更加庞大。
不容易理解:访问者模式的实现可能会使代码更加抽象和难以理解,特别是对于不熟悉该模式的开发人员来说。这可能增加维护和协作的难度。
不适用于所有情况:访问者模式主要用于处理对象结构中的元素和操作之间的复杂关系。如果对象结构相对简单或操作不太多变,使用访问者模式可能会显得过于繁琐。
破坏封装:在传统的面向对象设计中,封装是一个重要的原则,但访问者模式可能会破坏元素的封装,因为元素需要将自身传递给访问者,暴露了内部细节。
难以与其他模式结合:访问者模式可能与其他设计模式难以结合使用,尤其是那些强调对象间的松耦合和简单交互的模式。
性能开销:在某些情况下,访问者模式可能会引入额外的性能开销,特别是在处理大型对象结构时。访问者模式需要进行多次的遍历和调用,可能影响性能。
难以维护:由于访问者模式引入了多个类和接口,因此维护这些类之间的关系和一致性可能会变得复杂,尤其是在需要频繁修改操作时。
访问者模式是一种有用的设计模式,但并不适用于所有情况。在使用它时,需要仔细考虑系统的复杂性、可维护性和性能需求,以确保它能够真正提供价值并简化代码的设计和维护。
🔎3.使用场景
访问者模式(Visitor Pattern)适用于以下情况和使用场景:
对象结构复杂:当应用程序中存在一个复杂的对象结构,其中包含多种类型的元素,这些元素之间存在复杂的关联关系时,访问者模式可以帮助将操作与元素的数据结构解耦。
操作多样化:当应用程序需要对对象结构中的元素执行多种不同的操作,而且这些操作可能随时变化或需要扩展时,使用访问者模式可以更容易地添加新的操作,而无需修改现有的元素类。
数据结构稳定:访问者模式适用于情况下,元素的数据结构相对稳定,不经常变化。如果数据结构频繁变化,可能会导致访问者类的频繁修改,不利于维护。
不同操作的多态性:当需要在不同类型的元素上执行相同操作,但每个元素的实际行为却不同(多态性)时,访问者模式能够更好地支持这种需求。
维护一致性:如果希望确保在对象结构中的每个元素上都执行相同类型的操作,以维护一致性,访问者模式是一种合适的选择。
新增元素类型:当需要向对象结构中添加新的元素类型,而不希望修改现有的操作代码时,访问者模式非常有用。新的元素类型只需实现抽象元素接口,而不需要修改访问者类。
复杂的数据处理:在需要对对象结构中的元素进行复杂的数据处理和分析时,访问者模式可以提供一种结构化的方式来组织和执行这些操作。
不破坏封装:访问者模式允许在不破坏元素的封装的情况下执行操作,因为元素只需要提供一个接受访问者的方法,而不需要暴露内部状态。
访问者模式适用于处理复杂的对象结构和多样化的操作需求,它有助于提高代码的可维护性、可扩展性和灵活性。但在应用时,需要权衡其复杂性以及是否真正有必要引入该模式。
🚀感谢:给读者的一封信
亲爱的读者,
我在这篇文章中投入了大量的心血和时间,希望为您提供有价值的内容。这篇文章包含了深入的研究和个人经验,我相信这些信息对您非常有帮助。
如果您觉得这篇文章对您有所帮助,我诚恳地请求您考虑赞赏1元钱的支持。这个金额不会对您的财务状况造成负担,但它会对我继续创作高质量的内容产生积极的影响。
我之所以写这篇文章,是因为我热爱分享有用的知识和见解。您的支持将帮助我继续这个使命,也鼓励我花更多的时间和精力创作更多有价值的内容。
如果您愿意支持我的创作,请扫描下面二维码,您的支持将不胜感激。同时,如果您有任何反馈或建议,也欢迎与我分享。
再次感谢您的阅读和支持!
最诚挚的问候, “愚公搬代码”
- 点赞
- 收藏
- 关注作者
评论(0)