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

相关推荐
qqxhb1 小时前
零基础设计模式——行为型模式 - 命令模式
java·设计模式·go·命令模式
SAP-nkGavin2 小时前
ABAP设计模式之---“童子军法则(The Boy Scout Rule)”
设计模式·sap·abap
蔡蓝2 小时前
设计模式-适配器模式
java·设计模式·适配器模式
CodeCraft Studio3 小时前
Excel处理控件Aspose.Cells教程:使用 C# 在 Excel 中应用数据验证
c#·excel·aspose·文档开发·文档处理
少女续续念3 小时前
【DevOps】测试用例越来越多,质量反而更差了?谈谈现代软件测试的误区与转型
设计模式
DanmF--3 小时前
用C#实现单向链表和双向链表
数据结构·链表·c#
时光追逐者4 小时前
C#/.NET/.NET Core技术前沿周刊 | 第 41 期(2025年6.1-6.8)
c#·.net·.netcore
EP小良_0075 小时前
C# vs2022 找不到指定的 SDK“Microsof.NET.Sdk
android·c#·.net
蔡蓝6 小时前
设计模式-备忘录模式
java·设计模式·备忘录模式
我要打打代码6 小时前
0610_特性和反射_加密和解密_单例模式
单例模式·c#