示例工程【免费下载链接】Unity3DTraining【Unity杂货铺】unity大杂烩~项目地址https://gitcode.com/gh_mirrors/un/Unity3DTraining点击查看免费下载导读本文围绕 DesignPatterns/VisitorPattern 中记录的访问者模式展开系统梳理其定义、优缺点与适用场景并结合仓库中完整的 C# 控制台示例从「抽象访问者 / 抽象元素 / 结构对象」四个角色逐层拆解其协作流程。读完本文你将掌握访问者模式的核心调用链ObjectStructure.Accept - Element.Accept - Visitor.Visit并能在不修改元素类的前提下为既有对象结构添加新操作——这正是游戏开发中遍历角色、道具、UI 节点树等场景的常用手法。访问者模式的定义访问者模式Visitor Pattern表示一个作用于某对象结构中的各个元素的操作。它使你可以在不改变各个元素的类的前提下定义作用于这些元素的新操作。其核心价值在于把「数据结构」与「作用于结构上的操作」解耦。当对象结构稳定、而操作集合经常变化时访问者模式可以让你通过新增访问者类来扩展行为而不必逐个修改元素类。模式优缺点优点易于增加新的算法或操作集。新增一个访问者类即可为整个对象结构附加一套新操作原有元素类完全无需改动。缺点增加新的数据结构元素类变得很困难。每新增一个元素所有访问者类都必须同步增加对应的访问方法否则无法覆盖新元素。小结与适用场景访问者模式可以在不改变某个层级结构的类的前提下为这个层级结构添加一个新的操作。它适用于以下几种情况对象结构包含很多类对象但接口各不相同且希望对这些对象实施一些依赖于具体类的操作时需要为一个对象结构中的对象进行很多不同且互不相关的操作且这些操作可能随需求持续扩张时该对象结构被很多应用共享不希望因为新增操作而破坏共享结构时。仓库示例概览同一模式两种业务建模仓库中的 VisitorPattern 解决方案共包含两组实现均通过Program.Main直接驱动角色经典示例公司报表示例抽象访问者Visitor.csVisitor抽象类AbsVisitor.csIVisitor接口具体访问者ConcreteVisitorAA 主管、BB 主管、CEO抽象元素Element.csElementReporter.csReporter、Corporation.csIConporation具体元素ConcreteElementA、ConcreteElementBFinancialReport、LogisticsReport、PartA、PartB对象结构ObjectStructure.csReporter.csReportManager、Corporation.csObjectStructureA业务访问者领导视角—Leader.csFinanceDirector、SaleDirector从源码结构看经典示例忠实复刻了 GoF 四角色骨架而报表示例则用「领导Leader审阅报表Reporter」这一业务场景重新演绎了访问者模式是理解模式现实意义的直观案例。经典示例四角色协作模型抽象访问者与具体访问者Visitor.cs 定义了抽象访问者Visitor它为每一种具体元素声明一个访问方法/// summary /// 抽象访问者 /// /summary abstract class Visitor { public abstract void VisitConcreteElementA(ConcreteElementA element); public abstract void VisitConcreteElementB(ConcreteElementB element); } class ConcreteVisitor : Visitor { public override void VisitConcreteElementA(ConcreteElementA element) { Console.WriteLine(元素A被访问者访问); } public override void VisitConcreteElementB(ConcreteElementB element) { Console.WriteLine(元素B被访问者访问); } }注意这里的关键设计访问方法的参数是具体元素类型ConcreteElementA/ConcreteElementB而不是抽象类型Element。这使得访问者内部可以调用每个元素独有的方法实现「依赖于具体类的操作」代价则是每新增一个元素类都必须在此处补一个重载。抽象元素与具体元素Element.cs 定义了抽象元素Element及两个具体元素。元素的职责是接受访问者并把自身this回传给访问者的对应方法这就是著名的「双重分派」/// summary /// 抽象元素类 /// /summary abstract class Element { /// summary /// 抽象的接受访问的方法 /// /summary public abstract void Accept(Visitor visitor); } class ConcreteElementA : Element { public override void Accept(Visitor visitor) { visitor.VisitConcreteElementA(this); } /// summary /// 操作A /// /summary public void OperationA() { } }ConcreteElementA的Accept方法把this作为实参传给VisitConcreteElementA运行时既确定了访问者的真实类型第一重分派Accept(visitor)是多态调用又确定了元素的真实类型第二重分派VisitConcreteElementA(this)也是多态调用从而在一次遍历中完成「访问者 × 元素」的精确匹配。对象结构遍历的调度中枢ObjectStructure.cs 的注释将其定位为「对访问进行控制的管理者」。它维护一个元素集合并提供Attach/Detch添加移除元素以及统一的Accept遍历入口/// summary /// 结构对象类结构对象可以认为是一个对访问进行控制的管理者 /// /summary class ObjectStructure { private ListElement elements new ListElement(); public void Attach(Element element) { elements.Add(element); } public void Detch(Element element) { elements.Remove(element); } public void Accept(Visitor visitor) { foreach (var element in elements) { element.Accept(visitor); } } }Accept内部对集合做foreach遍历逐个调用元素的Accept最终把访问者分发到每个元素上。客户端只需把访问者对象交给ObjectStructure.Accept无需关心集合内部的遍历细节。客户端调用链Program.cs 演示了完整流程ObjectStructure obj new ObjectStructure(); obj.Attach(new ConcreteElementA()); obj.Attach(new ConcreteElementB()); ConcreteVisitor visitor new ConcreteVisitor(); obj.Accept(visitor);调用链可概括为ObjectStructure.Accept(visitor) └─ foreach element: element.Accept(visitor) └─ visitor.VisitConcreteElementX(this) // 双重分派公司报表示例把模式落到业务场景经典示例讲清了机制报表示例则展示了访问者模式在真实业务中的价值财务报表、物流报表的结构不变而审阅它们的角色财务总监、销售总监各自关心不同维度。报表稳定的对象结构Reporter.cs 定义了抽象报表Reporter与两类具体报表。FinancialReport持有收入、支出数据LogisticsReport持有生产、销售数据/// summary /// 财务报表 /// /summary class FinancialReport : Reporter { private int income 1000; private int expenditure 500; public override void Accecpt(Leader leader) { leader.VisitFinancialReport(this); } public int GetIncome() { return income; } public void SetExpenditure(int expenditure) { this.expenditure expenditure; } public int GetExpenditure() { return expenditure; } }注意SetExpenditure的存在——报表数据可以动态调整为后续扩展留了口子而接受访问的方法Accecpt(Leader)与经典示例的Accept(Visitor)如出一辙。ReportManager是该示例中的对象结构负责装载报表并统一派发访问class ReportManager { private ListReporter elements new ListReporter(); public void Attach(Reporter reporter) { elements.Add(reporter); } public void Detach(Reporter reporter) { elements.Remove(reporter); } public void Accept(Leader leader) { foreach (var element in elements) { element.Accecpt(leader); } } }领导关注点各异的访问者Leader.cs 把「访问者」具象化为领导角色。FinanceDirector财务总监只关心财务数据计算总收入SaleDirector销售总监只关心物流数据推算库存/// summary /// 抽象的领导类 /// /summary abstract class Leader { public abstract void VisitFinancialReport(FinancialReport report); public abstract void VisitLogisticsReport(LogisticsReport report); } /// summary /// 财务总监 /// /summary class FinanceDirector : Leader { public override void VisitFinancialReport(FinancialReport report) { int income report.GetIncome() - report.GetExpenditure(); Console.WriteLine(财务报表总收入 income); } public override void VisitLogisticsReport(LogisticsReport report) { //不关心销售报表 } } class SaleDirector : Leader { public override void VisitFinancialReport(FinancialReport report) { //不关心财务报表 } public override void VisitLogisticsReport(LogisticsReport report) { int stock report.GetProduction() - report.GetSale(); Console.WriteLine(根据销售报表库存为: stock); } }这个示例直观印证了 README 中的适用场景「需要为一个对象结构的对象进行很多不同的并且不相关的操作」——财务与库存分析互不相关却被组织在同一套报表结构上各自以空方法体表达「不关心」。报表示例的客户端调用ReportManager reportManager new ReportManager(); reportManager.Attach(new FinancialReport()); reportManager.Attach(new LogisticsReport()); FinanceDirector financeDirector new FinanceDirector(); SaleDirector saleDirector new SaleDirector(); reportManager.Accept(financeDirector); reportManager.Accept(saleDirector);同一份报表集合先后被财务总监与销售总监各遍历一次财务视角输出总收入1000 - 500 500销售视角输出库存800 - 400 400。报表类一行未改两套完全不同的分析逻辑便已接入——这正是访问者模式「在不改变元素类的前提下定义新操作」的直接证据。第三种变体公司分部与主管接口风格仓库还保留了另一套接口风格实现 Corporation.csPartA/PartB实现IConporation接口并暴露GetName()ObjectStructureA维护集合并通过AttVisitor/RemoveVisitor增删与之配对的是 AbsVisitor.cs 中的IVisitor接口及其三个实现interface IVisitor { void visit(PartA partA); void visit(PartB partB); } class A : IVisitor { public void visit(PartA partA) { Console.WriteLine(A主管 partA.GetName()); } public void visit(PartB partB) { //不关心B } } class CEO : IVisitor { public void visit(PartA partA) { Console.WriteLine(CEO主管 partA.GetName()); } public void visit(PartB partB) { Console.WriteLine(CEO主管 partB.GetName()); } }从源码结构看这组示例与报表示例角色一一对应但实现上改用接口而非抽象类A、B两位主管各管一个分部CEO则全盘接管——同样的结构、不同的关注粒度又一次体现了访问者「操作可独立扩展」的特性。需要留意的是该变体中PartA.Accept尚未实现完整逻辑Corporation.cs中抛出了NotImplementedException属于演示骨架阅读时可结合前两套完整示例理解其意图。小结何时使用访问者模式结合 README 与仓库源码使用访问者模式的决策要点可归纳为对象结构相对固定操作频繁扩张新增操作只需新增访问者类元素类与对象结构保持零修改需要对异构对象做依赖具体类型的处理利用双重分派AcceptVisit实现「访问者 × 元素」的精确路由替代繁琐的if/else类型判断多套互不相关的操作共享同一结构如财务分析、库存分析、导出、序列化等各自成类互不污染需要警惕的代价一旦元素类成为扩展点所有访问者都必须同步新增访问方法因此在元素结构可能频繁变化的设计中访问者模式并不划算。仓库中的三套示例经典四角色、公司报表、分部与主管分别从机制、业务与接口风格三个角度验证了同一模式的协作骨架ObjectStructure持有一组Element客户端将Visitor传入Accept后元素回传this触发访问者中对应的Visit方法。掌握了这条调用链你便可以在自己的 Unity 项目中对组件树、行为树、成就系统等稳定结构安全地叠加新逻辑。延伸阅读DesignPatterns 根目录仓库中其他设计模式单例、观察者、策略、命令等的同类示例GameProgrammingPatterns面向游戏开发的模式专题可与访问者模式交叉印证。赞分享示例工程【免费下载链接】Unity3DTraining【Unity杂货铺】unity大杂烩~项目地址https://gitcode.com/gh_mirrors/un/Unity3DTraining点击查看免费下载相关推荐Burn Flex 后端架构深度解析纯 Rust CPU 张量计算引擎的设计与实现Burn Flex 后端架构深度解析纯 Rust CPU 张量计算引擎的设计与实现 本文以 Burn 深度学习框架的 burn flex CPU 后端为对象示例工程Unity3DTraining 设计模式实战迭代器模式Iterator Pattern原理与 C 实现解析Unity3DTraining 设计模式实战迭代器模式Iterator Pattern原理与 C 实现解析 导读 本文围绕 Unity3DTraining示例工程Unity3DTraining 设计模式实战中介者模式Mediator PatternC 源码解析与应用指南Unity3DTraining 设计模式实战中介者模式Mediator PatternC 源码解析与应用指南 导读 本文以 Unity3DTraining示例工程上一篇5个实战技巧香山开源处理器FPGA部署完全指南下一篇SparkMD5 安装和配置指南创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
