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

文章目录

🧳 访问者模式(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. 定义元素与访问者接口
csharp 复制代码
public interface IItem {
    void Accept(IItemVisitor visitor);
}

public interface IItemVisitor {
    void Visit(Weapon weapon);
    void Visit(Potion potion);
    void Visit(QuestItem questItem);
}
2. 实现具体元素类
csharp 复制代码
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. 实现具体访问者
csharp 复制代码
// 伤害计算访问者
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. 对象结构管理
csharp 复制代码
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. 客户端使用
csharp 复制代码
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. 动态访问者注册
csharp 复制代码
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. 访问者组合模式
csharp 复制代码
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. 异步访问处理
csharp 复制代码
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. 成就系统触发

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

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

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

    csharp 复制代码
    public class UIDataVisitor : IItemVisitor {
        public void Visit(Weapon w) {
            InventoryUI.UpdateWeaponSlot(w);
        }
    }

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

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

八、最佳实践原则
  1. 元素接口稳定:避免频繁修改元素类接口

  2. 访问者单一职责:每个访问者专注一个功能领域

  3. 防御性访问 :处理未知元素类型

    csharp 复制代码
    public class SafeVisitor : IItemVisitor {
        public void Visit(IItem item) {
            if(item is Weapon w) VisitWeapon(w);
            else Debug.LogWarning($"未知物品类型:{item.GetType()}");
        }
    }
  4. 访问顺序控制

    csharp 复制代码
    public void ProcessItems(IItemVisitor visitor) {
        // 按优先级排序处理
        foreach(var item in _items.OrderBy(i => i.Priority)) {
            item.Accept(visitor);
        }
    }

九、常见问题解决方案

Q1:如何处理新增元素类型?

→ 使用反射扩展访问者

csharp 复制代码
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:如何避免循环依赖?

→ 引入中间接口层

csharp 复制代码
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:如何调试复杂访问流程?

→ 实现访问日志代理

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

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

相关推荐
阿蒙Amon9 天前
《C#图解教程 第5版》深度推荐
开发语言·c#
暖馒9 天前
C#委托与事件的区别
开发语言·c#
JosieBook9 天前
【C#】C#异步编程:异步延时 vs 阻塞延时深度对比
c#·多线程·异步·阻塞
甄天9 天前
WPF中MVVM和MVVMLight模式
c#·wpf·visual studio
切韵9 天前
Unity编辑器扩展:UI绑定复制工具
ui·unity·编辑器
勤奋的知更鸟9 天前
Java 编程之责任链模式
java·开发语言·设计模式·责任链模式
冰茶_9 天前
ASP.NET Core API文档与测试实战指南
后端·学习·http·ui·c#·asp.net
逆袭的菜鸟X10 天前
JS常用设计模式汇总
开发语言·javascript·设计模式
_oP_i10 天前
实现 “WebView2 获取word选中内容
开发语言·c#·word
Kookoos10 天前
ABP vNext + Azure Application Insights:APM 监控与性能诊断最佳实践
后端·c#·.net·abp vnext