【行为型之访问者模式】游戏开发实战——Unity灵活数据操作与跨系统交互的架构秘诀

news2025/5/16 0:00:05

文章目录

      • 🧳 访问者模式(Visitor Pattern)深度解析
        • 一、模式本质与核心价值
        • 二、经典UML结构
        • 三、Unity实战代码(游戏物品系统)
          • 1. 定义元素与访问者接口
          • 2. 实现具体元素类
          • 3. 实现具体访问者
          • 4. 对象结构管理
          • 5. 客户端使用
        • 四、模式进阶技巧
          • 1. 动态访问者注册
          • 2. 访问者组合模式
          • 3. 异步访问处理
        • 五、游戏开发典型应用场景
        • 六、性能优化策略
        • 七、模式对比与选择
        • 八、最佳实践原则
        • 九、常见问题解决方案

🧳 访问者模式(Visitor Pattern)深度解析

——以Unity实现灵活数据操作跨系统交互为核心案例


一、模式本质与核心价值

核心目标
分离数据结构与数据操作,支持在不修改元素类的前提下定义新操作
集中相关操作,避免污染元素类代码
实现双重分派,动态选择元素处理方法

关键术语

  • Visitor(访问者接口):声明访问各类元素的接口
  • ConcreteVisitor(具体访问者):实现特定操作的访问逻辑
  • Element(元素接口):定义接受访问者的方法
  • ObjectStructure(对象结构):维护元素集合,提供遍历接口

数学表达
设元素集合E = {e₁, e₂, …, eₙ},访问者V,则操作执行过程为:
∀e ∈ E, e.Accept(V) → V.Visit(e)


二、经典UML结构
accept
accept
«interface»
IVisitor
+VisitWeapon(Weapon)
+VisitPotion(Potion)
DamageCalculator
+VisitWeapon()
+VisitPotion()
«interface»
IItem
+Accept(IVisitor)
Weapon
+Accept()
Potion
+Accept()

三、Unity实战代码(游戏物品系统)
1. 定义元素与访问者接口
public interface IItem {
    void Accept(IItemVisitor visitor);
}

public interface IItemVisitor {
    void Visit(Weapon weapon);
    void Visit(Potion potion);
    void Visit(QuestItem questItem);
}
2. 实现具体元素类
public class Weapon : MonoBehaviour, IItem {
    public int Damage;
    public string ElementType;
    
    public void Accept(IItemVisitor visitor) {
        visitor.Visit(this);
    }
}

public class Potion : MonoBehaviour, IItem {
    public float HealAmount;
    public int Charges;
    
    public void Accept(IItemVisitor visitor) {
        visitor.Visit(this);
    }
}
3. 实现具体访问者
// 伤害计算访问者
public class DamageCalculator : IItemVisitor {
    private float _totalDamage;
    
    public void Visit(Weapon weapon) {
        _totalDamage += weapon.Damage * (weapon.ElementType == "Fire" ? 1.2f : 1f);
    }
    
    public void Visit(Potion potion) {
        // 药水不贡献伤害
    }
    
    public void Visit(QuestItem questItem) {
        // 任务物品不贡献伤害
    }
    
    public float GetTotalDamage() => _totalDamage;
}

// 存档序列化访问者
public class SaveVisitor : IItemVisitor {
    private List<byte[]> _serializedData = new();
    
    public void Visit(Weapon weapon) {
        var data = Encoding.UTF8.GetBytes($"Weapon|{weapon.Damage}|{weapon.ElementType}");
        _serializedData.Add(data);
    }
    
    public void Visit(Potion potion) {
        var data = Encoding.UTF8.GetBytes($"Potion|{potion.HealAmount}|{potion.Charges}");
        _serializedData.Add(data);
    }
    
    public byte[] GetSaveData() {
        return _serializedData.SelectMany(arr => arr).ToArray();
    }
}
4. 对象结构管理
public class InventorySystem : MonoBehaviour {
    private List<IItem> _items = new();
    
    public void AddItem(IItem item) => _items.Add(item);
    
    public void ProcessItems(IItemVisitor visitor) {
        foreach(var item in _items) {
            item.Accept(visitor);
        }
    }
}
5. 客户端使用
public class GameManager : MonoBehaviour {
    [SerializeField] private InventorySystem _inventory;
    
    void Start() {
        // 计算总伤害
        var damageCalc = new DamageCalculator();
        _inventory.ProcessItems(damageCalc);
        Debug.Log($"总伤害值:{damageCalc.GetTotalDamage()}");
        
        // 生成存档数据
        var saver = new SaveVisitor();
        _inventory.ProcessItems(saver);
        SaveToFile(saver.GetSaveData());
    }
}

四、模式进阶技巧
1. 动态访问者注册
public class DynamicVisitor : IItemVisitor {
    private Dictionary<Type, Action<object>> _handlers = new();
    
    public void RegisterHandler<T>(Action<T> handler) where T : IItem {
        _handlers[typeof(T)] = obj => handler((T)obj);
    }
    
    public void Visit(Weapon weapon) => InvokeHandler(weapon);
    public void Visit(Potion potion) => InvokeHandler(potion);
    
    private void InvokeHandler<T>(T item) where T : IItem {
        if(_handlers.TryGetValue(typeof(T), out var handler)) {
            handler(item);
        }
    }
}
2. 访问者组合模式
public class CompositeVisitor : IItemVisitor {
    private List<IItemVisitor> _visitors = new();
    
    public void AddVisitor(IItemVisitor visitor) => _visitors.Add(visitor);
    
    public void Visit(Weapon weapon) {
        foreach(var v in _visitors) v.Visit(weapon);
    }
    
    public void Visit(Potion potion) {
        foreach(var v in _visitors) v.Visit(potion);
    }
}
3. 异步访问处理
public class AsyncVisitor : MonoBehaviour, IItemVisitor {
    public async Task ProcessAsync(InventorySystem inventory) {
        var tasks = new List<Task>();
        foreach(var item in inventory.Items) {
            tasks.Add(Task.Run(() => item.Accept(this)));
        }
        await Task.WhenAll(tasks);
    }
    
    public void Visit(Weapon weapon) {
        // 异步处理武器
    }
}

五、游戏开发典型应用场景
  1. 成就系统触发

    public class AchievementVisitor : IItemVisitor {
        public void Visit(Weapon w) {
            if(w.Damage > 100) Unlock("POWER_WEAPON");
        }
    }
    
  2. 战斗伤害计算

    public class BattleDamageVisitor : IItemVisitor {
        private float _totalDamage;
        
        public void Visit(Weapon w) {
            _totalDamage += CalculateElementDamage(w);
        }
    }
    
  3. 场景序列化存档

    public class SceneSaveVisitor : IItemVisitor {
        private List<SerializableData> _sceneData = new();
        
        public void Visit(Enemy e) {
            _sceneData.Add(new EnemyData(e.Position, e.Health));
        }
    }
    
  4. UI数据绑定

    public class UIDataVisitor : IItemVisitor {
        public void Visit(Weapon w) {
            InventoryUI.UpdateWeaponSlot(w);
        }
    }
    

六、性能优化策略
策略实现方式适用场景
访问缓存缓存频繁访问结果复杂计算场景
批处理合并多个访问操作大量元素遍历
并行处理使用Job System并行访问CPU密集型操作
惰性求值延迟执行非关键访问性能敏感场景

七、模式对比与选择
维度访问者模式策略模式
关注点跨类操作算法替换
扩展方向新增操作新增算法
元素稳定性元素类需稳定策略可任意扩展
典型应用数据序列化战斗计算

八、最佳实践原则
  1. 元素接口稳定:避免频繁修改元素类接口
  2. 访问者单一职责:每个访问者专注一个功能领域
  3. 防御性访问:处理未知元素类型
    public class SafeVisitor : IItemVisitor {
        public void Visit(IItem item) {
            if(item is Weapon w) VisitWeapon(w);
            else Debug.LogWarning($"未知物品类型:{item.GetType()}");
        }
    }
    
  4. 访问顺序控制
    public void ProcessItems(IItemVisitor visitor) {
        // 按优先级排序处理
        foreach(var item in _items.OrderBy(i => i.Priority)) {
            item.Accept(visitor);
        }
    }
    

九、常见问题解决方案

Q1:如何处理新增元素类型?
→ 使用反射扩展访问者

public class ReflectionVisitor {
    private Dictionary<Type, MethodInfo> _methods = new();
    
    public void Visit(IItem item) {
        var type = item.GetType();
        if(_methods.TryGetValue(type, out var method)) {
            method.Invoke(this, new[]{item});
        }
    }
}

Q2:如何避免循环依赖?
→ 引入中间接口层

public interface IWeaponVisitor {
    void VisitWeapon(Weapon weapon);
}

public class DamageCalculator : IItemVisitor, IWeaponVisitor {
    public void Visit(Weapon w) => VisitWeapon(w);
    public void VisitWeapon(Weapon w) { /* 具体逻辑 */ }
}

Q3:如何调试复杂访问流程?
→ 实现访问日志代理

public class LoggingVisitorProxy : IItemVisitor {
    private IItemVisitor _wrapped;
    
    public void Visit(Weapon w) {
        Debug.Log($"开始处理武器:{w.Name}");
        _wrapped.Visit(w);
        Debug.Log("武器处理完成");
    }
}

上一篇 【行为型之模板方法模式】游戏开发实战——Unity标准化流程与可扩展架构的核心实现

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2376446.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Spring Spring Boot 常用注解整理

Spring & Spring Boot 常用注解整理 先理解核心概念&#xff1a;什么是注解&#xff08;Annotation&#xff09;&#xff1f;第一部分&#xff1a;IOC&#xff08;控制反转&#xff09;和 DI&#xff08;依赖注入&#xff09;1. Component2. Service, Repository, Controll…

c#建筑行业财务流水账系统软件可上传记账凭证财务管理系统签核功能

# financial_建筑行业 建筑行业财务流水账系统软件可上传记账凭证财务管理系统签核功能 # 开发背景 软件是给岳阳客户定制开发一款建筑行业流水账财务软件。提供工程签证单、施工日志、人员出勤表等信息记录。 # 财务管理系统功能描述 1.可以自行设置记账科目&#xff0c;做凭…

让 Cursor 教我写 MCP Client

文章目录 1. 写在最前面2. 动手实现一个 MCP Client2.1 How 天气查询 Client2.1.1 向 Cursor 提问的艺术2.1.2 最终成功展示2.1.3 client 的代码 3. MCP 协议核心之一总结3.1 SSE vs WebSocket 4. 碎碎念5. 参考资料 1. 写在最前面 学习了 MCP Server 的实现后&#xff0c;刚好…

反射, 注解, 动态代理

文章目录 单元测试什么是单元测试咱们之前是如何进行单元测试的&#xff1f; 有啥问题 &#xff1f;现在使用方法进行测试优点Junit单元测试的使用步骤删除不需要的jar包总结 反射认识反射、获取类什么是反射反射具体学什么&#xff1f;反射第一步&#xff1a;或者Class对象 获…

vue vite 无法热更新问题

一、在vue页面引入组件CustomEmployeesDialog&#xff0c;修改组件CustomEmployeesDialog无法热更新 引入方式&#xff1a; import CustomEmployeesDialog from ../dialog/customEmployeesDialog.vue 目录结构&#xff1a; 最后发现是引入import时&#xff0c;路径大小写与目…

深度学习中的查全率与查准率:如何实现有效权衡

&#x1f4cc; 友情提示&#xff1a; 本文内容由银河易创AI&#xff08;https://ai.eaigx.com&#xff09;创作平台的gpt-4-turbo模型辅助生成&#xff0c;旨在提供技术参考与灵感启发。文中观点或代码示例需结合实际情况验证&#xff0c;建议读者通过官方文档或实践进一步确认…

Windows玩游戏的时候,一按字符键就显示桌面

最近打赛伯朋克 2077 的时候&#xff0c;不小心按错键了&#xff0c;导致一按字符键就显示桌面。如下&#xff1a; 一开始我以为是输入法的问题&#xff08;相信打游戏的人都知道输入法和奔跑键冲突的时候有多烦&#xff09;&#xff0c;但是后来解决半天发现并不是。在网上搜…

Gemini 2.5 Flash和Pro预览版价格以及上下文缓存的理解

Gemini 2.5 Flash和Pro预览版价格 Gemini 2.5 Flash 预览版就是 Google 的最新 AI 大模型&#xff0c;能处理巨量内容。可以免费体验&#xff0c;但有次数和功能上的限制&#xff1b;付费层级才开放全部高级功能。价格也比传统 API 略有不同&#xff0c;尤其在“思考预算”“上…

vue2 头像上传+裁剪组件封装

背景&#xff1a;最近在进行公司业务开发时&#xff0c;遇到了头像上传限制尺寸的需求&#xff0c;即限制为一寸证件照&#xff08;宽295像素&#xff0c;高413像素&#xff09;。 用到的第三方库&#xff1a; "vue-cropper": "^0.5.5" 完整组件代码&…

AI-02a5a5.神经网络-与学习相关的技巧-权重初始值

权重的初始值 在神经网络的学习中&#xff0c;权重的初始值特别重要。实际上&#xff0c;设定什么样的权重初始值&#xff0c;经常关系到神经网络的学习能否成功。 不要将权重初始值设为 0 权值衰减&#xff08;weight decay&#xff09;&#xff1a;抑制过拟合、提高泛化能…

【springcloud学习(dalston.sr1)】Eureka单个服务端的搭建(含源代码)(三)

该系列项目整体介绍及源代码请参照前面写的一篇文章【springcloud学习(dalston.sr1)】项目整体介绍&#xff08;含源代码&#xff09;&#xff08;一&#xff09; 这篇文章主要介绍单个eureka服务端的集群环境是如何搭建的。 通过前面的文章【springcloud学习(dalston.sr1)】…

Node.js数据抓取技术实战示例

Node.js常用的库有哪些呢&#xff1f;比如axios或者node-fetch用来发送HTTP请求&#xff0c;cheerio用来解析HTML&#xff0c;如果是动态网页的话可能需要puppeteer这样的无头浏览器。这些工具的组合应该能满足大部分需求。 然后&#xff0c;可能遇到的难点在哪里&#xff1f;…

windows10 安装 QT

本地环境有个qt文件&#xff0c;这里是5.14.2 打开一个cmd窗口并指定到该文件根目录下 .\qt-opensource-windows-x86-5.14.2.exe --mirror https://mirrors.ustc.edu.cn/qtproject 执行上面命令 记住是文件名&#xff0c;记住不要傻 X的直接复制&#xff0c;是你的文件名 点击…

WordPress 和 GPL – 您需要了解的一切

如果您使用 WordPress&#xff0c;GPL 对您来说应该很重要&#xff0c;您也应该了解它。查看有关 WordPress 和 GPL 的最全面指南。 您可能听说过 GPL&#xff08;通常被称为 WordPress 的权利法案&#xff09;&#xff0c;但很可能并不完全了解它。这是有道理的–这是一个复杂…

C++书本摆放 2024年信息素养大赛复赛 C++小学/初中组 算法创意实践挑战赛 真题详细解析

目录 C++书本摆放 一、题目要求 1、编程实现 2、输入输出 二、算法分析 三、程序编写 四、运行结果 五、考点分析 六、 推荐资料 1、C++资料 2、Scratch资料 3、Python资料 C++书本摆放 2024年信息素养大赛 C++复赛真题 一、题目要求 1、编程实现 中科智慧科技…

RabbitMQ 核心概念与消息模型深度解析(一)

一、RabbitMQ 是什么 在当今分布式系统盛行的时代&#xff0c;消息队列作为一种至关重要的中间件技术&#xff0c;扮演着实现系统之间异步通信、解耦和削峰填谷等关键角色 。RabbitMQ 便是消息队列领域中的佼佼者&#xff0c;是一个开源的消息代理和队列服务器&#xff0c;基于…

论文阅读笔记——双流网络

双流网络论文 视频相比图像包含更多信息&#xff1a;运动信息、时序信息、背景信息等等。 原先处理视频的方法&#xff1a; CNN LSTM&#xff1a;CNN 抽取关键特征&#xff0c;LSTM 做时序逻辑&#xff1b;抽取视频中关键 K 帧输入 CNN 得到图片特征&#xff0c;再输入 LSTM&…

LabVIEW在电子电工教学中的应用

在电子电工教学领域&#xff0c;传统教学模式面临诸多挑战&#xff0c;如实验设备数量有限、实验过程存在安全隐患、教学内容更新滞后等。LabVIEW 作为一款功能强大的图形化编程软件&#xff0c;为解决这些问题提供了创新思路&#xff0c;在电子电工教学的多个关键环节发挥着重…

Vue3 怎么在ElMessage消息提示组件中添加自定义icon图标

1、定义icon组件代码&#xff1a; <template><svg :class"svgClass" aria-hidden"true"><use :xlink:href"iconName" :fill"color"/></svg> </template><script> export default defineComponen…

生活破破烂烂,AI 缝缝补补(附提示词)

写在前面&#xff1a;​【Fire 计算器】已上线&#xff0c;快算算财富自由要多少​ 现实不总温柔&#xff0c;愿你始终自渡。 请永远拯救自己于水火之中。 毛绒风格提示词&#xff08;供参考&#xff09;&#xff1a; 1. 逼真毛绒风 Transform this image into a hyperrealist…