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

相关推荐
Aman19841 小时前
Claude Code做Unity项目移植(二): 自动执行只读操作,告别频繁的
unity·游戏开发·vibe coding·claude code
泛凡(Linyongui)8 小时前
基于 CW32L010 的智能厨卫调温旋钮方案--低成本 EC21 编码旋钮全解析
单片机·嵌入式硬件·c#
暴走约伯10 小时前
【VFX工具链源码拆解 02】告别机械劳动(上):一键统改百个子粒子颜色与批量重命名
3d·unity·c#·材质
郝学胜-神的一滴11 小时前
游戏设计:原型与开发 18:Unity入门的第一课
人工智能·程序人生·游戏·ui·unity·游戏引擎
信息安全失业大专人员12 小时前
每日了解一个智能体-WorkBuddy
开发语言·ai·c#·智能体
呆呆敲代码的小Y12 小时前
【实战精选面试题】C# 面试题详细解答,按难易程度排序
android·java·游戏·c#·游戏开发·unity面试题·c#面试题
geovindu12 小时前
rust: Semaphore Pattern
开发语言·后端·设计模式·rust·代理模式·企业级信号量模式·同步型模式
是2的10次方啊13 小时前
设计模式与生活⑧:快递到了,别一直刷物流
设计模式
淡海水13 小时前
09-01-内存-Unity资源生命周期全景
unity·c#·游戏引擎·.netcore·gc
椒颜皮皮虾྅16 小时前
让 C# 读懂整张图:DeploySharp 驱动 PaddleOCR v4/v5/v6,打通 OCR 全流程
开发语言·人工智能·深度学习·学习·c#·ocr·cuda