【行为型之访问者模式】游戏开发实战——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标准化流程与可扩展架构的核心实现

相关推荐
明耀4 小时前
WPF C# 用WebView加载H5页面(uniapp项目,vue项目)
uni-app·c#·wpf
我不是程序猿儿8 小时前
【C#】 lock 关键字
java·开发语言·c#
动感光博12 小时前
Unity序列化字段、单例模式(Singleton Pattern)
unity·单例模式·c#
黑洞视界14 小时前
NCC Mocha v0.2.0 发布, 新增对 Metrics 的支持
c#·.net·可观测性·observability
FAREWELL0007514 小时前
Unity基础学习(十五)核心系统——音效系统
学习·unity·c#·游戏引擎
zimoyin15 小时前
Java 快速转 C# 教程
java·开发语言·c#
向宇it16 小时前
【unity游戏开发——编辑器扩展】使用MenuItem自定义菜单栏拓展
开发语言·ui·unity·c#·编辑器·游戏引擎
动感光博16 小时前
Unity碰撞检测(射线投射、胶囊体投射)、交互(图层、掩码)
unity·c#·游戏引擎·游戏程序·动画