# 架构设计模式指南 > 全面介绍 GFramework 中的架构设计模式,帮助你构建清晰、可维护、可扩展的游戏架构。 ## 📋 目录 - [概述](#概述) - [MVC 模式](#mvc-模式) - [MVVM 模式](#mvvm-模式) - [命令模式](#命令模式) - [查询模式](#查询模式) - [事件驱动模式](#事件驱动模式) - [依赖注入模式](#依赖注入模式) - [服务定位器模式](#服务定位器模式) - [对象池模式](#对象池模式) - [状态模式](#状态模式) - [设计原则](#设计原则) - [架构分层](#架构分层) - [依赖管理](#依赖管理) - [事件系统设计](#事件系统设计) - [模块化架构](#模块化架构) - [错误处理策略](#错误处理策略) - [测试策略](#测试策略) - [重构指南](#重构指南) - [模式选择与组合](#模式选择与组合) - [常见问题](#常见问题) ## 概述 架构设计模式是经过验证的解决方案,用于解决软件开发中的常见问题。GFramework 内置了多种设计模式,帮助你构建高质量的游戏应用。 ### 为什么需要架构设计模式? 1. **提高代码质量**:遵循最佳实践,减少 bug 2. **增强可维护性**:清晰的结构,易于理解和修改 3. **促进团队协作**:统一的代码风格和架构 4. **提升可扩展性**:轻松添加新功能 5. **简化测试**:解耦的组件更容易测试 ### GFramework 支持的核心模式 | 模式 | 用途 | 核心组件 | |-----------|--------------|-------------------------| | **MVC** | 分离数据、视图和控制逻辑 | Model, Controller | | **MVVM** | 数据绑定和响应式 UI | Model, BindableProperty | | **命令模式** | 封装操作请求 | ICommand, CommandBus | | **查询模式** | 分离读操作 | IQuery, QueryBus | | **事件驱动** | 松耦合通信 | IEventBus, Event | | **依赖注入** | 控制反转 | IIocContainer | | **服务定位器** | 服务查找 | Architecture.GetSystem | | **对象池** | 对象复用 | IObjectPoolSystem | | **状态模式** | 状态管理 | IStateMachine | ## MVC 模式 ### 概念 MVC(Model-View-Controller)是一种将应用程序分为三个核心组件的架构模式: - **Model(模型)**:管理数据和业务逻辑 - **View(视图)**:显示数据给用户 - **Controller(控制器)**:处理用户输入,协调 Model 和 View ### 在 GFramework 中的实现 ```csharp // Model - 数据层 public class PlayerModel : AbstractModel { public BindableProperty<int> Health { get; } = new(100); public BindableProperty<int> Score { get; } = new(0); public BindableProperty<string> Name { get; } = new("Player"); protected override void OnInit() { // 监听数据变化 Health.Register(newHealth => { if (newHealth <= 0) { this.SendEvent(new PlayerDiedEvent()); } }); } } // Controller - 控制层 [ContextAware] public partial class PlayerController : Node, IController { private PlayerModel _playerModel; public override void _Ready() { _playerModel = this.GetModel<PlayerModel>(); // 监听数据变化,更新视图 _playerModel.Health.Register(UpdateHealthUI); _playerModel.Score.Register(UpdateScoreUI); } // 处理用户输入 public override void _Input(InputEvent @event) { if (@event is InputEventKey keyEvent && keyEvent.Pressed) { if (keyEvent.Keycode == Key.Space) { // 发送命令修改 Model this.SendCommand(new AttackCommand()); } } } // 更新视图 private void UpdateHealthUI(int health) { var healthBar = GetNode<ProgressBar>("HealthBar"); healthBar.Value = health; } private void UpdateScoreUI(int score) { var scoreLabel = GetNode<Label>("ScoreLabel"); scoreLabel.Text = $"Score: {score}"; } } // System - 业务逻辑层(可选) public class CombatSystem : AbstractSystem { protected override void OnInit() { this.RegisterEvent<AttackCommand>(OnAttack); } private void OnAttack(AttackCommand cmd) { var playerModel = this.GetModel<PlayerModel>(); var enemyModel = this.GetModel<EnemyModel>(); // 计算伤害 int damage = CalculateDamage(playerModel, enemyModel); enemyModel.Health.Value -= damage; // 增加分数 if (enemyModel.Health.Value <= 0) { playerModel.Score.Value += 100; } } private int CalculateDamage(PlayerModel player, EnemyModel enemy) { return 10; // 简化示例 } } ``` ### MVC 优势 - ✅ **职责分离**:Model、View、Controller 各司其职 - ✅ **易于测试**:可以独立测试每个组件 - ✅ **可维护性高**:修改一个组件不影响其他组件 - ✅ **支持多视图**:同一个 Model 可以有多个 View ### 最佳实践 1. **Model 只负责数据**:不包含 UI 逻辑 2. **Controller 协调交互**:不直接操作 UI 细节 3. **View 只负责显示**:不包含业务逻辑 4. **使用事件通信**:Model 变化通过事件通知 Controller ## MVVM 模式 ### 概念 MVVM(Model-View-ViewModel)是 MVC 的变体,强调数据绑定和响应式编程: - **Model**:数据和业务逻辑 - **View**:用户界面 - **ViewModel**:View 的抽象,提供数据绑定 ### 在 GFramework 中的实现 ```csharp // Model - 数据层 public class GameModel : AbstractModel { public BindableProperty<int> CurrentLevel { get; } = new(1); public BindableProperty<float> Progress { get; } = new(0f); public BindableProperty<bool> IsLoading { get; } = new(false); protected override void OnInit() { Progress.Register(progress => { if (progress >= 1.0f) { this.SendEvent(new LevelCompletedEvent()); } }); } } // ViewModel - 视图模型(在 GFramework 中,Model 本身就是 ViewModel) public class PlayerViewModel : AbstractModel { private PlayerModel _playerModel; // 计算属性 public BindableProperty<string> HealthText { get; } = new(""); public BindableProperty<float> HealthPercentage { get; } = new(1.0f); public BindableProperty<bool> IsAlive { get; } = new(true); protected override void OnInit() { _playerModel = this.GetModel<PlayerModel>(); // 绑定数据转换 _playerModel.Health.Register(health => { HealthText.Value = $"{health} / {_playerModel.MaxHealth.Value}"; HealthPercentage.Value = (float)health / _playerModel.MaxHealth.Value; IsAlive.Value = health > 0; }); } } // View - 视图层 [ContextAware] public partial class PlayerView : Control, IController { private PlayerViewModel _viewModel; private Label _healthLabel; private ProgressBar _healthBar; private Panel _deathPanel; public override void _Ready() { _viewModel = this.GetModel<PlayerViewModel>(); _healthLabel = GetNode<Label>("HealthLabel"); _healthBar = GetNode<ProgressBar>("HealthBar"); _deathPanel = GetNode<Panel>("DeathPanel"); // 数据绑定 _viewModel.HealthText.Register(text => _healthLabel.Text = text); _viewModel.HealthPercentage.Register(pct => _healthBar.Value = pct * 100); _viewModel.IsAlive.Register(alive => _deathPanel.Visible = !alive); } } ``` ### MVVM 优势 - ✅ **自动更新 UI**:数据变化自动反映到界面 - ✅ **减少样板代码**:不需要手动更新 UI - ✅ **易于测试**:ViewModel 可以独立测试 - ✅ **支持复杂 UI**:适合数据驱动的界面 ### 最佳实践 1. **使用 BindableProperty**:实现响应式数据 2. **ViewModel 不依赖 View**:保持单向依赖 3. **计算属性放在 ViewModel**:如百分比、格式化文本 4. **避免在 View 中写业务逻辑**:只负责数据绑定 ## 命令模式 ### 概念 命令模式将请求封装为对象,从而支持参数化、队列化、日志记录和撤销操作。 ### 在 GFramework 中的实现 ```csharp // 定义命令输入 public class BuyItemInput : ICommandInput { public string ItemId { get; set; } public int Quantity { get; set; } } // 实现命令 public class BuyItemCommand : AbstractCommand<BuyItemInput> { protected override void OnExecute(BuyItemInput input) { var playerModel = this.GetModel<PlayerModel>(); var inventoryModel = this.GetModel<InventoryModel>(); var shopModel = this.GetModel<ShopModel>(); // 获取物品信息 var item = shopModel.GetItem(input.ItemId); var totalCost = item.Price * input.Quantity; // 检查金币 if (playerModel.Gold.Value < totalCost) { this.SendEvent(new InsufficientGoldEvent()); return; } // 扣除金币 playerModel.Gold.Value -= totalCost; // 添加物品 inventoryModel.AddItem(input.ItemId, input.Quantity); // 发送事件 this.SendEvent(new ItemPurchasedEvent { ItemId = input.ItemId, Quantity = input.Quantity, Cost = totalCost }); } } // 使用命令 [ContextAware] public partial class ShopController : IController { public void OnBuyButtonClicked(string itemId, int quantity) { // 创建并发送命令 var input = new BuyItemInput { ItemId = itemId, Quantity = quantity }; this.SendCommand(new BuyItemCommand { Input = input }); } } ``` ### 支持撤销的命令 ```csharp public interface IUndoableCommand : ICommand { void Undo(); } public class MoveCommand : AbstractCommand, IUndoableCommand { private Vector2 _previousPosition; private Vector2 _newPosition; public MoveCommand(Vector2 newPosition) { _newPosition = newPosition; } protected override void OnExecute() { var playerModel = this.GetModel<PlayerModel>(); _previousPosition = playerModel.Position.Value; playerModel.Position.Value = _newPosition; } public void Undo() { var playerModel = this.GetModel<PlayerModel>(); playerModel.Position.Value = _previousPosition; } } // 命令历史管理器 public class CommandHistory { private readonly Stack<IUndoableCommand> _history = new(); public void Execute(IUndoableCommand command) { command.Execute(); _history.Push(command); } public void Undo() { if (_history.Count > 0) { var command = _history.Pop(); command.Undo(); } } } ``` ### 命令模式优势 - ✅ **解耦发送者和接收者**:调用者不需要知道实现细节 - ✅ **支持撤销/重做**:保存命令历史 - ✅ **支持队列和日志**:可以记录所有操作 - ✅ **易于扩展**:添加新命令不影响现有代码 ### 最佳实践 1. **命令保持原子性**:一个命令完成一个完整操作 2. **使用输入对象传参**:避免构造函数参数过多 3. **命令无状态**:执行完即可丢弃 4. **发送事件通知结果**:而不是返回值 ## 查询模式 ### 概念 查询模式(CQRS 的一部分)将读操作与写操作分离,查询只读取数据,不修改状态。 ### 在 GFramework 中的实现 ```csharp // 定义查询输入 public class GetPlayerStatsInput : IQueryInput { public string PlayerId { get; set; } } // 定义查询结果 public class PlayerStats { public int Level { get; set; } public int Health { get; set; } public int MaxHealth { get; set; } public int Attack { get; set; } public int Defense { get; set; } public int TotalPower { get; set; } } // 实现查询 public class GetPlayerStatsQuery : AbstractQuery<GetPlayerStatsInput, PlayerStats> { protected override PlayerStats OnDo(GetPlayerStatsInput input) { var playerModel = this.GetModel<PlayerModel>(); var equipmentModel = this.GetModel<EquipmentModel>(); // 计算总战力 int basePower = playerModel.Level.Value * 10; int equipmentPower = equipmentModel.GetTotalPower(); return new PlayerStats { Level = playerModel.Level.Value, Health = playerModel.Health.Value, MaxHealth = playerModel.MaxHealth.Value, Attack = playerModel.Attack.Value + equipmentModel.GetAttackBonus(), Defense = playerModel.Defense.Value + equipmentModel.GetDefenseBonus(), TotalPower = basePower + equipmentPower }; } } // 使用查询 [ContextAware] public partial class CharacterPanelController : IController { public void ShowCharacterStats() { var input = new GetPlayerStatsInput { PlayerId = "player1" }; var query = new GetPlayerStatsQuery { Input = input }; var stats = this.SendQuery(query); // 显示统计信息 DisplayStats(stats); } private void DisplayStats(PlayerStats stats) { Console.WriteLine($"Level: {stats.Level}"); Console.WriteLine($"Health: {stats.Health}/{stats.MaxHealth}"); Console.WriteLine($"Attack: {stats.Attack}"); Console.WriteLine($"Defense: {stats.Defense}"); Console.WriteLine($"Total Power: {stats.TotalPower}"); } } ``` ### 复杂查询示例 ```csharp // 查询背包中可装备的物品 public class GetEquippableItemsQuery : AbstractQuery<EmptyQueryInput, List<Item>> { protected override List<Item> OnDo(EmptyQueryInput input) { var inventoryModel = this.GetModel<InventoryModel>(); var playerModel = this.GetModel<PlayerModel>(); return inventoryModel.GetAllItems() .Where(item => item.Type == ItemType.Equipment) .Where(item => item.RequiredLevel <= playerModel.Level.Value) .OrderByDescending(item => item.Power) .ToList(); } } // 组合查询 public class CanUseSkillQuery : AbstractQuery<CanUseSkillInput, bool> { protected override bool OnDo(CanUseSkillInput input) { var playerModel = this.GetModel<PlayerModel>(); // 查询技能消耗 var costQuery = new GetSkillCostQuery { Input = new GetSkillCostInput { SkillId = input.SkillId } }; var cost = this.SendQuery(costQuery); // 查询冷却状态 var cooldownQuery = new IsSkillOnCooldownQuery { Input = new IsSkillOnCooldownInput { SkillId = input.SkillId } }; var onCooldown = this.SendQuery(cooldownQuery); // 综合判断 return playerModel.Mana.Value >= cost.ManaCost && !onCooldown && playerModel.Health.Value > 0; } } ``` ### 查询模式优势 - ✅ **职责分离**:读写操作明确分离 - ✅ **易于优化**:可以针对查询进行缓存优化 - ✅ **提高可读性**:查询意图清晰 - ✅ **支持复杂查询**:可以组合多个简单查询 ### 最佳实践 1. **查询只读取,不修改**:保持查询的纯粹性 2. **使用清晰的命名**:Get、Is、Can、Has 等前缀 3. **避免过度查询**:频繁查询考虑使用 BindableProperty 4. **合理使用缓存**:复杂计算结果可以缓存 ## 事件驱动模式 ### 概念 事件驱动模式通过事件实现组件间的松耦合通信。发送者不需要知道接收者,接收者通过订阅事件来响应变化。 ### 在 GFramework 中的实现 ```csharp // 定义事件 public struct PlayerDiedEvent { public Vector3 Position { get; set; } public string Cause { get; set; } public int FinalScore { get; set; } } public struct EnemyKilledEvent { public string EnemyId { get; set; } public int Reward { get; set; } } // Model 发送事件 public class PlayerModel : AbstractModel { public BindableProperty<int> Health { get; } = new(100); public BindableProperty<Vector3> Position { get; } = new(Vector3.Zero); protected override void OnInit() { Health.Register(newHealth => { if (newHealth <= 0) { // 发送玩家死亡事件 this.SendEvent(new PlayerDiedEvent { Position = Position.Value, Cause = "Health depleted", FinalScore = this.GetModel<GameModel>().Score.Value }); } }); } } // System 监听和发送事件 public class AchievementSystem : AbstractSystem { private int _enemyKillCount = 0; protected override void OnInit() { // 监听敌人被杀事件 this.RegisterEvent<EnemyKilledEvent>(OnEnemyKilled); // 监听玩家死亡事件 this.RegisterEvent<PlayerDiedEvent>(OnPlayerDied); } private void OnEnemyKilled(EnemyKilledEvent e) { _enemyKillCount++; // 检查成就条件 if (_enemyKillCount == 10) { this.SendEvent(new AchievementUnlockedEvent { AchievementId = "first_blood_10", Title = "新手猎人", Description = "击败10个敌人" }); } } private void OnPlayerDied(PlayerDiedEvent e) { // 记录统计数据 var statsModel = this.GetModel<StatisticsModel>(); statsModel.RecordDeath(e.Position, e.Cause); } } // Controller 监听事件 [ContextAware] public partial class UIController : IController { private IUnRegisterList _unregisterList = new UnRegisterList(); public void Initialize() { // 监听成就解锁事件 this.RegisterEvent(OnAchievementUnlocked) .AddToUnregisterList(_unregisterList); // 监听玩家死亡事件 this.RegisterEvent(OnPlayerDied) .AddToUnregisterList(_unregisterList); } private void OnAchievementUnlocked(AchievementUnlockedEvent e) { ShowAchievementNotification(e.Title, e.Description); } private void OnPlayerDied(PlayerDiedEvent e) { ShowGameOverScreen(e.FinalScore); } public void Cleanup() { _unregisterList.UnRegisterAll(); } } ``` ### 事件组合 ```csharp using GFramework.SourceGenerators.Abstractions.rule; // 使用 OrEvent 组合多个事件 [ContextAware] public partial class InputController : IController { public void Initialize() { var onAnyInput = new OrEvent() .Or(keyboardEvent) .Or(mouseEvent) .Or(gamepadEvent); onAnyInput.Register(() => { ResetIdleTimer(); }); } } ``` ### 事件驱动模式优势 - ✅ **松耦合**:发送者和接收者互不依赖 - ✅ **一对多通信**:一个事件可以有多个监听者 - ✅ **易于扩展**:添加新监听者不影响现有代码 - ✅ **支持异步**:事件可以异步处理 ### 最佳实践 1. **事件命名使用过去式**:PlayerDiedEvent、LevelCompletedEvent 2. **事件使用结构体**:减少内存分配 3. **及时注销事件**:使用 IUnRegisterList 管理 4. **避免事件循环**:事件处理器中谨慎发送新事件 ## 依赖注入模式 ### 概念 依赖注入(DI)是一种实现控制反转(IoC)的技术,通过外部注入依赖而不是在类内部创建。 ### 在 GFramework 中的实现 ```csharp // 定义接口 public interface IStorageService { Task SaveAsync<T>(string key, T data); Task<T> LoadAsync<T>(string key); } public interface IAudioService { void PlaySound(string soundId); void PlayMusic(string musicId); } // 实现服务 public class LocalStorageService : IStorageService { public async Task SaveAsync<T>(string key, T data) { var json = JsonSerializer.Serialize(data); await File.WriteAllTextAsync($"saves/{key}.json", json); } public async Task<T> LoadAsync<T>(string key) { var json = await File.ReadAllTextAsync($"saves/{key}.json"); return JsonSerializer.Deserialize<T>(json); } } public class GodotAudioService : IAudioService { private AudioStreamPlayer _soundPlayer; private AudioStreamPlayer _musicPlayer; public void PlaySound(string soundId) { var sound = GD.Load<AudioStream>($"res://sounds/{soundId}.ogg"); _soundPlayer.Stream = sound; _soundPlayer.Play(); } public void PlayMusic(string musicId) { var music = GD.Load<AudioStream>($"res://music/{musicId}.ogg"); _musicPlayer.Stream = music; _musicPlayer.Play(); } } // 在架构中注册服务 public class GameArchitecture : Architecture { protected override void Init() { // 注册服务实现 RegisterUtility<IStorageService>(new LocalStorageService()); RegisterUtility<IAudioService>(new GodotAudioService()); // 注册 System(System 会自动获取依赖) RegisterSystem(new SaveSystem()); RegisterSystem(new AudioSystem()); } } // System 使用依赖注入 public class SaveSystem : AbstractSystem { private IStorageService _storageService; protected override void OnInit() { // 从容器获取依赖 _storageService = this.GetUtility<IStorageService>(); this.RegisterEvent<SaveGameEvent>(OnSaveGame); } private async void OnSaveGame(SaveGameEvent e) { var playerModel = this.GetModel<PlayerModel>(); var saveData = new SaveData { PlayerName = playerModel.Name.Value, Level = playerModel.Level.Value, Health = playerModel.Health.Value }; await _storageService.SaveAsync("current_save", saveData); } } ``` ### 构造函数注入(推荐) ```csharp public class SaveSystem : AbstractSystem { private readonly IStorageService _storageService; private readonly IAudioService _audioService; // 通过构造函数注入依赖 public SaveSystem(IStorageService storageService, IAudioService audioService) { _storageService = storageService; _audioService = audioService; } protected override void OnInit() { this.RegisterEvent<SaveGameEvent>(OnSaveGame); } private async void OnSaveGame(SaveGameEvent e) { await _storageService.SaveAsync("save", e.Data); _audioService.PlaySound("save_success"); } } // 注册时传入依赖 public class GameArchitecture : Architecture { protected override void Init() { var storageService = new LocalStorageService(); var audioService = new GodotAudioService(); RegisterUtility<IStorageService>(storageService); RegisterUtility<IAudioService>(audioService); // 构造函数注入 RegisterSystem(new SaveSystem(storageService, audioService)); } } ``` ### 依赖注入优势 - ✅ **易于测试**:可以注入模拟对象 - ✅ **松耦合**:依赖接口而非实现 - ✅ **灵活配置**:运行时选择实现 - ✅ **提高可维护性**:依赖关系清晰 ### 最佳实践 1. **依赖接口而非实现**:使用 IStorageService 而非 LocalStorageService 2. **优先使用构造函数注入**:依赖关系更明确 3. **避免循环依赖**:System 不应相互依赖 4. **使用 IoC 容器管理生命周期**:让框架管理对象创建 ## 服务定位器模式 ### 概念 服务定位器模式提供一个全局访问点来获取服务,是依赖注入的替代方案。 ### 在 GFramework 中的实现 ```csharp // GFramework 的 Architecture 本身就是服务定位器 public class GameplaySystem : AbstractSystem { protected override void OnInit() { // 通过服务定位器获取服务 var playerModel = this.GetModel<PlayerModel>(); var audioSystem = this.GetSystem<AudioSystem>(); var storageUtility = this.GetUtility<IStorageUtility>(); // 使用服务 playerModel.Health.Value = 100; audioSystem.PlayBGM("gameplay"); } } // 在 Controller 中使用 [ContextAware] public partial class MenuController : IController { public void OnStartButtonClicked() { // 通过架构获取服务 var gameModel = this.GetModel(); gameModel.GameState.Value = GameState.Playing; // 发送命令 this.SendCommand(new StartGameCommand()); } } ``` ### 自定义服务定位器 ```csharp // 创建专门的服务定位器 public static class ServiceLocator { private static readonly Dictionary<Type, object> _services = new(); public static void Register<T>(T service) { _services[typeof(T)] = service; } public static T Get<T>() { if (_services.TryGetValue(typeof(T), out var service)) { return (T)service; } throw new InvalidOperationException($"Service {typeof(T)} not registered"); } public static void Clear() { _services.Clear(); } } // 使用自定义服务定位器 public class GameInitializer { public void Initialize() { // 注册服务 ServiceLocator.Register<IAnalyticsService>(new AnalyticsService()); ServiceLocator.Register<ILeaderboardService>(new LeaderboardService()); } } public class GameOverScreen { public void SubmitScore(int score) { // 获取服务 var leaderboard = ServiceLocator.Get<ILeaderboardService>(); leaderboard.SubmitScore(score); var analytics = ServiceLocator.Get<IAnalyticsService>(); analytics.TrackEvent("game_over", new { score }); } } ``` ### 服务定位器 vs 依赖注入 | 特性 | 服务定位器 | 依赖注入 | |-----------|--------------|------------| | **依赖可见性** | 隐式(运行时获取) | 显式(构造函数参数) | | **易用性** | 简单直接 | 需要配置 | | **测试性** | 较难(需要模拟全局状态) | 容易(注入模拟对象) | | **编译时检查** | 无 | 有 | | **适用场景** | 快速原型、小项目 | 大型项目、团队协作 | ### 最佳实践 1. **小项目使用服务定位器**:简单直接 2. **大项目使用依赖注入**:更易维护 3. **避免过度使用**:不要把所有东西都放入定位器 4. **提供清晰的 API**:GetModel、GetSystem、GetUtility ## 对象池模式 ### 概念 对象池模式通过复用对象来减少内存分配和垃圾回收,提高性能。 ### 在 GFramework 中的实现 ```csharp // 定义可池化对象 public class Bullet : Node2D, IPoolableNode { public bool IsInPool { get; set; } public void OnSpawn() { // 从池中取出时调用 Visible = true; IsInPool = false; } public void OnRecycle() { // 回收到池中时调用 Visible = false; IsInPool = true; Position = Vector2.Zero; Rotation = 0; } } // 创建对象池系统 public class BulletPoolSystem : AbstractNodePoolSystem<Bullet> { protected override Bullet CreateInstance() { var bullet = new Bullet(); // 初始化子弹 return bullet; } protected override void OnInit() { // 预创建对象 PrewarmPool(50); } } // 使用对象池 public class WeaponSystem : AbstractSystem { private BulletPoolSystem _bulletPool; protected override void OnInit() { _bulletPool = this.GetSystem<BulletPoolSystem>(); this.RegisterEvent<FireWeaponEvent>(OnFireWeapon); } private void OnFireWeapon(FireWeaponEvent e) { // 从池中获取子弹 var bullet = _bulletPool.Spawn(); bullet.Position = e.Position; bullet.Rotation = e.Direction; // 3秒后回收 ScheduleRecycle(bullet, 3.0f); } private async void ScheduleRecycle(Bullet bullet, float delay) { await Task.Delay((int)(delay * 1000)); _bulletPool.Recycle(bullet); } } ``` ### 通用对象池 ```csharp // 通用对象池实现 public class ObjectPool<T> where T : class, new() { private readonly Stack<T> _pool = new(); private readonly Action<T> _onSpawn; private readonly Action<T> _onRecycle; private readonly int _maxSize; public ObjectPool(int initialSize = 10, int maxSize = 100, Action<T> onSpawn = null, Action<T> onRecycle = null) { _maxSize = maxSize; _onSpawn = onSpawn; _onRecycle = onRecycle; // 预创建对象 for (int i = 0; i < initialSize; i++) { _pool.Push(new T()); } } public T Spawn() { T obj = _pool.Count > 0 ? _pool.Pop() : new T(); _onSpawn?.Invoke(obj); return obj; } public void Recycle(T obj) { if (_pool.Count < _maxSize) { _onRecycle?.Invoke(obj); _pool.Push(obj); } } public void Clear() { _pool.Clear(); } } // 使用通用对象池 public class ParticleSystem : AbstractSystem { private ObjectPool<Particle> _particlePool; protected override void OnInit() { _particlePool = new ObjectPool<Particle>( initialSize: 100, maxSize: 500, onSpawn: p => p.Reset(), onRecycle: p => p.Clear() ); } public void EmitParticles(Vector3 position, int count) { for (int i = 0; i < count; i++) { var particle = _particlePool.Spawn(); particle.Position = position; particle.Velocity = GetRandomVelocity(); } } } ``` ### 对象池优势 - ✅ **减少 GC 压力**:复用对象,减少内存分配 - ✅ **提高性能**:避免频繁创建销毁对象 - ✅ **稳定帧率**:减少 GC 导致的卡顿 - ✅ **适合高频对象**:子弹、粒子、特效等 ### 最佳实践 1. **预热池**:提前创建对象避免运行时分配 2. **设置最大容量**:防止池无限增长 3. **重置对象状态**:OnRecycle 中清理状态 4. **监控池使用情况**:记录 Spawn/Recycle 次数 ## 状态模式 ### 概念 状态模式允许对象在内部状态改变时改变其行为,将状态相关的行为封装到独立的状态类中。 ### 在 GFramework 中的实现 ```csharp // 定义游戏状态 public class MenuState : ContextAwareStateBase { public override void OnEnter(IState from) { Console.WriteLine("进入菜单状态"); // 显示菜单 UI var uiSystem = this.GetSystem<UISystem>(); uiSystem.ShowMenu(); // 播放菜单音乐 var audioSystem = this.GetSystem<AudioSystem>(); audioSystem.PlayBGM("menu_theme"); } public override void OnExit(IState to) { Console.WriteLine("退出菜单状态"); // 隐藏菜单 UI var uiSystem = this.GetSystem<UISystem>(); uiSystem.HideMenu(); } public override bool CanTransitionTo(IState target) { // 菜单可以转换到游戏或设置状态 return target is GameplayState or SettingsState; } } public class GameplayState : ContextAwareStateBase { public override void OnEnter(IState from) { Console.WriteLine("进入游戏状态"); // 初始化游戏 var gameModel = this.GetModel<GameModel>(); gameModel.Reset(); // 加载关卡 this.SendCommand(new LoadLevelCommand { LevelId = 1 }); // 播放游戏音乐 var audioSystem = this.GetSystem<AudioSystem>(); audioSystem.PlayBGM("gameplay_theme"); } public override void OnExit(IState to) { Console.WriteLine("退出游戏状态"); // 保存游戏进度 this.SendCommand(new SaveGameCommand()); } public override bool CanTransitionTo(IState target) { // 游戏中可以暂停或结束 return target is PauseState or GameOverState; } } public class PauseState : ContextAwareStateBase { public override void OnEnter(IState from) { Console.WriteLine("进入暂停状态"); // 暂停游戏 var timeSystem = this.GetSystem<TimeSystem>(); timeSystem.Pause(); // 显示暂停菜单 var uiSystem = this.GetSystem<UISystem>(); uiSystem.ShowPauseMenu(); } public override void OnExit(IState to) { Console.WriteLine("退出暂停状态"); // 恢复游戏 var timeSystem = this.GetSystem<TimeSystem>(); timeSystem.Resume(); // 隐藏暂停菜单 var uiSystem = this.GetSystem<UISystem>(); uiSystem.HidePauseMenu(); } public override bool CanTransitionTo(IState target) { // 暂停只能返回游戏或退出到菜单 return target is GameplayState or MenuState; } } // 注册状态机 public class GameArchitecture : Architecture { protected override void Init() { // 创建状态机系统 var stateMachine = new StateMachineSystem(); // 注册所有状态 stateMachine .Register(new MenuState()) .Register(new GameplayState()) .Register(new PauseState()) .Register(new GameOverState()) .Register(new SettingsState()); RegisterSystem<IStateMachineSystem>(stateMachine); } } // 使用状态机 [ContextAware] public partial class GameController : IController { public async Task StartGame() { var stateMachine = this.GetSystem(); await stateMachine.ChangeToAsync(); } public async Task PauseGame() { var stateMachine = this.GetSystem(); await stateMachine.ChangeToAsync(); } public async Task ResumeGame() { var stateMachine = this.GetSystem(); await stateMachine.ChangeToAsync(); } } ``` ### 异步状态 ```csharp public class LoadingState : AsyncContextAwareStateBase { public override async Task OnEnterAsync(IState from) { Console.WriteLine("开始加载..."); // 显示加载界面 var uiSystem = this.GetSystem<UISystem>(); uiSystem.ShowLoadingScreen(); // 异步加载资源 await LoadResourcesAsync(); Console.WriteLine("加载完成"); // 自动切换到游戏状态 var stateMachine = this.GetSystem<IStateMachineSystem>(); await stateMachine.ChangeToAsync<GameplayState>(); } private async Task LoadResourcesAsync() { // 模拟异步加载 await Task.Delay(2000); } public override async Task OnExitAsync(IState to) { // 隐藏加载界面 var uiSystem = this.GetSystem<UISystem>(); uiSystem.HideLoadingScreen(); await Task.CompletedTask; } } ``` ### 状态模式优势 - ✅ **清晰的状态管理**:每个状态独立封装 - ✅ **易于扩展**:添加新状态不影响现有代码 - ✅ **状态转换验证**:CanTransitionTo 控制合法转换 - ✅ **支持异步操作**:异步状态处理加载等操作 ### 最佳实践 1. **状态保持单一职责**:每个状态只负责一个场景 2. **使用转换验证**:防止非法状态转换 3. **在 OnEnter 初始化,OnExit 清理**:保持状态独立 4. **异步操作使用异步状态**:避免阻塞主线程 ## 设计原则 ### 1. 单一职责原则 (SRP) 确保每个类只负责一个功能领域: ```csharp // ✅ 好的做法:职责单一 public class PlayerMovementController : AbstractSystem { protected override void OnInit() { this.RegisterEvent(OnPlayerInput); } private void OnPlayerInput(PlayerInputEvent e) { // 只负责移动逻辑 ProcessMovement(e.Direction); } private void ProcessMovement(Vector2 direction) { // 移动相关的业务逻辑 } } public class PlayerCombatController : AbstractSystem { protected override void OnInit() { this.RegisterEvent(OnAttackInput); } private void OnAttackInput(AttackInputEvent e) { // 只负责战斗逻辑 ProcessAttack(e.Target); } private void ProcessAttack(Entity target) { // 战斗相关的业务逻辑 } } // ❌ 避免:职责混乱 public class PlayerController : AbstractSystem { private void OnPlayerInput(PlayerInputEvent e) { // 移动逻辑 ProcessMovement(e.Direction); // 战斗逻辑 if (e.IsAttacking) { ProcessAttack(e.Target); } // UI逻辑 UpdateHealthBar(); // 音效逻辑 PlaySoundEffect(); // 存档逻辑 SaveGame(); // 职责太多,难以维护 } } ``` ### 2. 开闭原则 (OCP) 设计应该对扩展开放,对修改封闭: ```csharp // ✅ 好的做法:使用接口和策略模式 public interface IWeaponStrategy { void Attack(Entity attacker, Entity target); int CalculateDamage(Entity attacker, Entity target); } public class SwordWeaponStrategy : IWeaponStrategy { public void Attack(Entity attacker, Entity target) { var damage = CalculateDamage(attacker, target); target.TakeDamage(damage); PlaySwingAnimation(); } public int CalculateDamage(Entity attacker, Entity target) { return attacker.Strength + GetSwordBonus() - target.Armor; } } public class MagicWeaponStrategy : IWeaponStrategy { public void Attack(Entity attacker, Entity target) { var damage = CalculateDamage(attacker, target); target.TakeDamage(damage); CastMagicEffect(); } public int CalculateDamage(Entity attacker, Entity target) { return attacker.Intelligence * 2 + GetMagicBonus() - target.MagicResistance; } } public class CombatSystem : AbstractSystem { private readonly Dictionary _weaponStrategies; public CombatSystem() { _weaponStrategies = new() { { WeaponType.Sword, new SwordWeaponStrategy() }, { WeaponType.Magic, new MagicWeaponStrategy() } }; } public void Attack(Entity attacker, Entity target) { var weaponType = attacker.EquippedWeapon.Type; if (_weaponStrategies.TryGetValue(weaponType, out var strategy)) { strategy.Attack(attacker, target); } } // 添加新武器类型时,只需要添加新的策略,不需要修改现有代码 public void RegisterWeaponStrategy(WeaponType type, IWeaponStrategy strategy) { _weaponStrategies[type] = strategy; } } // ❌ 避免:需要修改现有代码来扩展 public class CombatSystem : AbstractSystem { public void Attack(Entity attacker, Entity target) { var weaponType = attacker.EquippedWeapon.Type; switch (weaponType) { case WeaponType.Sword: // 剑的攻击逻辑 break; case WeaponType.Bow: // 弓的攻击逻辑 break; default: throw new NotSupportedException($"Weapon type {weaponType} not supported"); } // 添加新武器类型时需要修改这里的 switch 语句 } } ``` ### 3. 依赖倒置原则 (DIP) 高层模块不应该依赖低层模块,两者都应该依赖抽象: ```csharp // ✅ 好的做法:依赖抽象 public interface IDataStorage { Task SaveAsync<T>(string key, T data); Task<T> LoadAsync<T>(string key, T defaultValue = default); Task ExistsAsync(string key); } public class FileStorage : IDataStorage { public async Task SaveAsync<T>(string key, T data) { var json = JsonConvert.SerializeObject(data); await File.WriteAllTextAsync(GetFilePath(key), json); } public async Task<T> LoadAsync<T>(string key, T defaultValue = default) { var filePath = GetFilePath(key); if (!File.Exists(filePath)) return defaultValue; var json = await File.ReadAllTextAsync(filePath); return JsonConvert.DeserializeObject<T>(json); } public async Task ExistsAsync(string key) { return File.Exists(GetFilePath(key)); } private string GetFilePath(string key) { return $"saves/{key}.json"; } } public class CloudStorage : IDataStorage { public async Task SaveAsync<T>(string key, T data) { // 云存储实现 await UploadToCloud(key, data); } public async Task<T> LoadAsync<T>(string key, T defaultValue = default) { // 云存储实现 return await DownloadFromCloud<T>(key, defaultValue); } public async Task ExistsAsync(string key) { // 云存储实现 return await CheckCloudExists(key); } } // 高层模块依赖抽象 public class SaveSystem : AbstractSystem { private readonly IDataStorage _storage; public SaveSystem(IDataStorage storage) { _storage = storage; } public async Task SaveGameAsync(SaveData data) { await _storage.SaveAsync("current_save", data); } public async Task LoadGameAsync() { return await _storage.LoadAsync("current_save"); } } // ❌ 避免:依赖具体实现 public class SaveSystem : AbstractSystem { private readonly FileStorage _storage; // 直接依赖具体实现 public SaveSystem() { _storage = new FileStorage(); // 硬编码依赖 } // 无法轻松切换到其他存储方式 } ``` ## 架构分层 ### 1. 清晰的层次结构 ```csharp // ✅ 好的做法:清晰的分层架构 namespace Game.Models { // 数据层:只负责存储状态 public class PlayerModel : AbstractModel { public BindableProperty Health { get; } = new(100); public BindableProperty MaxHealth { get; } = new(100); public BindableProperty Position { get; } = new(Vector2.Zero); public BindableProperty State { get; } = new(PlayerState.Idle); protected override void OnInit() { // 只处理数据相关的逻辑 Health.Register(OnHealthChanged); } private void OnHealthChanged(int newHealth) { if (newHealth <= 0) { State.Value = PlayerState.Dead; SendEvent(new PlayerDeathEvent()); } } } public enum PlayerState { Idle, Moving, Attacking, Dead } } namespace Game.Systems { // 业务逻辑层:处理游戏逻辑 public class PlayerMovementSystem : AbstractSystem { private PlayerModel _playerModel; private GameModel _gameModel; protected override void OnInit() { _playerModel = GetModel(); _gameModel = GetModel(); this.RegisterEvent(OnPlayerInput); } private void OnPlayerInput(PlayerInputEvent e) { if (_gameModel.State.Value != GameState.Playing) return; if (_playerModel.State.Value == PlayerState.Dead) return; // 处理移动逻辑 ProcessMovement(e.Direction); } private void ProcessMovement(Vector2 direction) { if (direction != Vector2.Zero) { _playerModel.Position.Value += direction.Normalized() * GetMovementSpeed(); _playerModel.State.Value = PlayerState.Moving; SendEvent(new PlayerMovedEvent { NewPosition = _playerModel.Position.Value, Direction = direction }); } else { _playerModel.State.Value = PlayerState.Idle; } } private float GetMovementSpeed() { // 从玩家属性或其他地方获取速度 return 5.0f; } } } namespace Game.Controllers { // 控制层:连接用户输入和业务逻辑 [ContextAware] public partial class PlayerController : Node, IController { private PlayerModel _playerModel; public override void _Ready() { _playerModel = this.GetModel(); // 监听用户输入 SetProcessInput(true); // 监听数据变化,更新UI _playerModel.Health.Register(UpdateHealthUI); _playerModel.Position.Register(UpdatePosition); } public override void _Input(InputEvent @event) { if (@event is InputEventKey keyEvent && keyEvent.Pressed) { var direction = GetInputDirection(keyEvent); this.SendEvent(new PlayerInputEvent { Direction = direction }); } } private void UpdateHealthUI(int health) { // 更新UI显示 var healthBar = GetNode("UI/HealthBar"); healthBar.Value = (float)health / _playerModel.MaxHealth.Value * 100; } private void UpdatePosition(Vector2 position) { // 更新玩家位置 Position = position; } private Vector2 GetInputDirection(InputEventKey keyEvent) { return keyEvent.Keycode switch { Key.W => Vector2.Up, Key.S => Vector2.Down, Key.A => Vector2.Left, Key.D => Vector2.Right, _ => Vector2.Zero }; } } } ``` ### 2. 避免层次混乱 ```csharp // ❌ 避免:层次混乱 public class PlayerController : Node, IController { // 混合了数据层、业务逻辑层和控制层的职责 public BindableProperty Health { get; } = new(100); // 数据层职责 public override void _Input(InputEvent @event) // 控制层职责 { if (@event is InputEventKey keyEvent && keyEvent.Pressed) { if (keyEvent.Keycode == Key.W) { Position += Vector2.Up * MovementSpeed; // 业务逻辑层职责 } if (keyEvent.Keycode == Key.Space) { Health -= 10; // 业务逻辑层职责 PlaySoundEffect(); // 业务逻辑层职责 } } } // 这样会导致代码难以测试和维护 } ``` ## 依赖管理 ### 1. 构造函数注入 ```csharp // ✅ 好的做法:构造函数注入 public class PlayerCombatSystem : AbstractSystem { private readonly PlayerModel _playerModel; private readonly IWeaponService _weaponService; private readonly ISoundService _soundService; private readonly IEffectService _effectService; // 通过构造函数注入依赖 public PlayerCombatSystem( PlayerModel playerModel, IWeaponService weaponService, ISoundService soundService, IEffectService effectService) { _playerModel = playerModel; _weaponService = weaponService; _soundService = soundService; _effectService = effectService; } protected override void OnInit() { this.RegisterEvent(OnAttack); } private void OnAttack(AttackEvent e) { var weapon = _weaponService.GetEquippedWeapon(_playerModel); var damage = _weaponService.CalculateDamage(weapon, e.Target); e.Target.TakeDamage(damage); _soundService.PlayAttackSound(weapon.Type); _effectService.PlayAttackEffect(_playerModel.Position, weapon.Type); } } // ❌ 避免:依赖注入容器 public class PlayerCombatSystem : AbstractSystem { private PlayerModel _playerModel; private IWeaponService _weaponService; private ISoundService _soundService; private IEffectService _effectService; protected override void OnInit() { // 在运行时获取依赖,难以测试 _playerModel = GetModel(); _weaponService = GetService(); _soundService = GetService(); _effectService = GetService(); } // 测试时难以模拟依赖 } ``` ### 2. 接口隔离 ```csharp // ✅ 好的做法:小而专注的接口 public interface IMovementController { void Move(Vector2 direction); void Stop(); bool CanMove(); } public interface ICombatController { void Attack(Entity target); void Defend(); bool CanAttack(); } public interface IUIController { void ShowHealthBar(); void HideHealthBar(); void UpdateHealthDisplay(int currentHealth, int maxHealth); } public class PlayerController : Node, IMovementController, ICombatController, IUIController { // 实现各个接口,职责清晰 } // ❌ 避免:大而全的接口 public interface IPlayerController { void Move(Vector2 direction); void Stop(); void Attack(Entity target); void Defend(); void ShowHealthBar(); void HideHealthBar(); void UpdateHealthDisplay(int currentHealth, int maxHealth); void SaveGame(); void LoadGame(); void Respawn(); void PlayAnimation(string animationName); void StopAnimation(); // ... 更多方法,接口过于庞大 } ``` ## 事件系统设计 ### 1. 事件命名和结构 ```csharp // ✅ 好的做法:清晰的事件命名和结构 public struct PlayerHealthChangedEvent { public int PreviousHealth { get; } public int NewHealth { get; } public int MaxHealth { get; } public Vector3 DamagePosition { get; } public DamageType DamageType { get; } } public struct PlayerDiedEvent { public Vector3 DeathPosition { get; } public string CauseOfDeath { get; } public TimeSpan SurvivalTime { get; } } public struct WeaponEquippedEvent { public string PlayerId { get; } public WeaponType WeaponType { get; } public string WeaponId { get; } } // ❌ 避免:模糊的事件命名和结构 public struct PlayerEvent { public EventType Type { get; } public object Data { get; } // 类型不安全 public Dictionary Properties { get; } // 难以理解 } ``` ### 2. 事件处理职责 ```csharp // ✅ 好的做法:单一职责的事件处理 public class UIHealthBarController : AbstractSystem { protected override void OnInit() { this.RegisterEvent(OnPlayerHealthChanged); this.RegisterEvent(OnPlayerDied); } private void OnPlayerHealthChanged(PlayerHealthChangedEvent e) { UpdateHealthBar(e.NewHealth, e.MaxHealth); if (e.NewHealth < e.PreviousHealth) { ShowDamageEffect(e.DamagePosition, e.PreviousHealth - e.NewHealth); } } private void OnPlayerDied(PlayerDiedEvent e) { HideHealthBar(); ShowDeathScreen(e.CauseOfDeath); } } public class AudioController : AbstractSystem { protected override void OnInit() { this.RegisterEvent(OnPlayerHealthChanged); this.RegisterEvent(OnPlayerDied); } private void OnPlayerHealthChanged(PlayerHealthChangedEvent e) { if (e.NewHealth < e.PreviousHealth) { PlayHurtSound(e.DamageType); } } private void OnPlayerDied(PlayerDiedEvent e) { PlayDeathSound(); } } // ❌ 避免:一个处理器处理多种不相关的事件 public class PlayerEventHandler : AbstractSystem { protected override void OnInit() { this.RegisterEvent(OnPlayerHealthChanged); this.RegisterEvent(OnPlayerDied); this.RegisterEvent(OnWeaponEquipped); this.RegisterEvent(OnLevelUp); // 注册太多事件,职责混乱 } private void OnPlayerHealthChanged(PlayerHealthChangedEvent e) { UpdateUI(); // UI职责 PlayAudio(); // 音频职责 SaveStatistics(); // 存档职责 UpdateAchievements(); // 成就系统职责 // 一个事件处理器承担太多职责 } } ``` ## 模块化架构 ### 1. 模块边界清晰 ```csharp // ✅ 好的做法:清晰的模块边界 public class AudioModule : AbstractModule { // 模块只负责音频相关的功能 public override void Install(IArchitecture architecture) { architecture.RegisterSystem(new AudioSystem()); architecture.RegisterSystem(new MusicSystem()); architecture.RegisterUtility(new AudioUtility()); } } public class InputModule : AbstractModule { // 模块只负责输入相关的功能 public override void Install(IArchitecture architecture) { architecture.RegisterSystem(new InputSystem()); architecture.RegisterSystem(new InputMappingSystem()); architecture.RegisterUtility(new InputUtility()); } } public class UIModule : AbstractModule { // 模块只负责UI相关的功能 public override void Install(IArchitecture architecture) { architecture.RegisterSystem(new UISystem()); architecture.RegisterSystem(new HUDSystem()); architecture.RegisterSystem(new MenuSystem()); architecture.RegisterUtility(new UIUtility()); } } // ❌ 避免:模块职责混乱 public class GameModule : AbstractModule { public override void Install(IArchitecture architecture) { // 一个模块包含所有功能 architecture.RegisterSystem(new AudioSystem()); // 音频 architecture.RegisterSystem(new InputSystem()); // 输入 architecture.RegisterSystem(new UISystem()); // UI architecture.RegisterSystem(new CombatSystem()); // 战斗 architecture.RegisterSystem(new InventorySystem()); // 背包 architecture.RegisterSystem(new QuestSystem()); // 任务 // 模块过于庞大,难以维护 } } ``` ### 2. 模块间通信 ```csharp // ✅ 好的做法:通过事件进行模块间通信 public class AudioModule : AbstractModule { public override void Install(IArchitecture architecture) { architecture.RegisterSystem(new AudioSystem()); } } public class AudioSystem : AbstractSystem { protected override void OnInit() { // 监听其他模块发送的事件 this.RegisterEvent(OnPlayerAttack); this.RegisterEvent(OnPlayerDied); this.RegisterEvent(OnWeaponEquipped); } private void OnPlayerAttack(PlayerAttackEvent e) { PlayAttackSound(e.WeaponType); } private void OnPlayerDied(PlayerDiedEvent e) { PlayDeathSound(); } private void OnWeaponEquipped(WeaponEquippedEvent e) { PlayEquipSound(e.WeaponType); } } public class CombatModule : AbstractModule { public override void Install(IArchitecture architecture) { architecture.RegisterSystem(new CombatSystem()); } } public class CombatSystem : AbstractSystem { protected override void OnInit() { this.RegisterEvent(OnAttackInput); } private void OnAttackInput(AttackInputEvent e) { ProcessAttack(e); // 发送事件通知其他模块 SendEvent(new PlayerAttackEvent { PlayerId = e.PlayerId, WeaponType = GetPlayerWeaponType(e.PlayerId) }); } } // ❌ 避免:模块间直接依赖 public class CombatSystem : AbstractSystem { private AudioSystem _audioSystem; // 直接依赖其他模块 protected override void OnInit() { // 直接获取其他模块的系统 _audioSystem = GetSystem(); } private void OnAttackInput(AttackInputEvent e) { ProcessAttack(e); // 直接调用其他模块的方法 _audioSystem.PlayAttackSound(weaponType); } } ``` ## 错误处理策略 ### 1. 异常处理层次 ```csharp // ✅ 好的做法:分层异常处理 public class GameApplicationException : Exception { public string ErrorCode { get; } public Dictionary Context { get; } public GameApplicationException(string message, string errorCode, Dictionary context = null, Exception innerException = null) : base(message, innerException) { ErrorCode = errorCode; Context = context ?? new Dictionary(); } } public class PlayerException : GameApplicationException { public PlayerException(string message, string errorCode, Dictionary context = null, Exception innerException = null) : base(message, errorCode, context, innerException) { } } public class InventoryException : GameApplicationException { public InventoryException(string message, string errorCode, Dictionary context = null, Exception innerException = null) : base(message, errorCode, context, innerException) { } } // 在系统中的使用 public class PlayerInventorySystem : AbstractSystem { public void AddItem(string playerId, Item item) { try { ValidateItem(item); CheckInventorySpace(playerId, item); AddItemToInventory(playerId, item); SendEvent(new ItemAddedEvent { PlayerId = playerId, Item = item }); } catch (ItemValidationException ex) { throw new InventoryException( $"Failed to add item {item.Id} to player {playerId}", "ITEM_VALIDATION_FAILED", new Dictionary { ["playerId"] = playerId, ["itemId"] = item.Id, ["validationError"] = ex.Message }, ex ); } catch (InventoryFullException ex) { throw new InventoryException( $"Player {playerId} inventory is full", "INVENTORY_FULL", new Dictionary { ["playerId"] = playerId, ["itemId"] = item.Id, ["maxSlots"] = ex.MaxSlots, ["currentSlots"] = ex.CurrentSlots }, ex ); } catch (Exception ex) { // 捕获未知异常并包装 throw new InventoryException( $"Unexpected error adding item {item.Id} to player {playerId}", "UNKNOWN_ERROR", new Dictionary { ["playerId"] = playerId, ["itemId"] = item.Id, ["originalError"] = ex.Message }, ex ); } } private void ValidateItem(Item item) { if (item == null) throw new ItemValidationException("Item cannot be null"); if (string.IsNullOrEmpty(item.Id)) throw new ItemValidationException("Item ID cannot be empty"); if (item.StackSize <= 0) throw new ItemValidationException("Item stack size must be positive"); } private void CheckInventorySpace(string playerId, Item item) { var inventory = GetPlayerInventory(playerId); var requiredSpace = CalculateRequiredSpace(item); if (inventory.FreeSpace < requiredSpace) { throw new InventoryFullException( inventory.FreeSpace, inventory.MaxSlots ); } } } ``` ### 2. 错误恢复策略 ```csharp // ✅ 好的做法:优雅的错误恢复 public class SaveSystem : AbstractSystem { private readonly IStorage _primaryStorage; private readonly IStorage _backupStorage; public SaveSystem(IStorage primaryStorage, IStorage backupStorage = null) { _primaryStorage = primaryStorage; _backupStorage = backupStorage ?? new LocalStorage("backup"); } public async Task LoadSaveDataAsync(string saveId) { try { // 尝试从主存储加载 return await _primaryStorage.ReadAsync(saveId); } catch (StorageException ex) { Logger.Warning($"Failed to load from primary storage: {ex.Message}"); try { // 尝试从备份存储加载 var backupData = await _backupStorage.ReadAsync(saveId); Logger.Info($"Successfully loaded from backup storage: {saveId}"); // 恢复到主存储 await _primaryStorage.WriteAsync(saveId, backupData); return backupData; } catch (Exception backupEx) { Logger.Error($"Failed to load from backup storage: {backupEx.Message}"); // 返回默认存档数据 return GetDefaultSaveData(); } } } private SaveData GetDefaultSaveData() { Logger.Warning("Returning default save data due to loading failures"); return new SaveData { PlayerId = "default", Level = 1, Health = 100, Position = Vector3.Zero, CreatedAt = DateTime.UtcNow }; } } // ❌ 避免:粗暴的错误处理 public class SaveSystem : AbstractSystem { public async Task LoadSaveDataAsync(string saveId) { try { return await _storage.ReadAsync(saveId); } catch (Exception ex) { // 直接抛出异常,不提供恢复机制 throw new Exception($"Failed to load save: {ex.Message}", ex); } } } ``` ## 测试策略 ### 1. 可测试的架构设计 ```csharp // ✅ 好的做法:可测试的架构 public interface IPlayerMovementService { void MovePlayer(string playerId, Vector2 direction); bool CanPlayerMove(string playerId); } public class PlayerMovementService : IPlayerMovementService { private readonly IPlayerRepository _playerRepository; private readonly ICollisionService _collisionService; private readonly IMapService _mapService; public PlayerMovementService( IPlayerRepository playerRepository, ICollisionService collisionService, IMapService mapService) { _playerRepository = playerRepository; _collisionService = collisionService; _mapService = mapService; } public void MovePlayer(string playerId, Vector2 direction) { if (!CanPlayerMove(playerId)) return; var player = _playerRepository.GetById(playerId); var newPosition = player.Position + direction * player.Speed; if (_collisionService.CanMoveTo(newPosition)) { player.Position = newPosition; _playerRepository.Update(player); } } public bool CanPlayerMove(string playerId) { var player = _playerRepository.GetById(playerId); return player != null && player.IsAlive && !player.IsStunned; } } // 测试代码 [TestFixture] public class PlayerMovementServiceTests { private Mock _mockPlayerRepository; private Mock _mockCollisionService; private Mock _mockMapService; private PlayerMovementService _movementService; [SetUp] public void Setup() { _mockPlayerRepository = new Mock(); _mockCollisionService = new Mock(); _mockMapService = new Mock(); _movementService = new PlayerMovementService( _mockPlayerRepository.Object, _mockCollisionService.Object, _mockMapService.Object ); } [Test] public void MovePlayer_ValidMovement_ShouldUpdatePlayerPosition() { // Arrange var playerId = "player1"; var player = new Player { Id = playerId, Position = Vector2.Zero, Speed = 5.0f }; var direction = Vector2.Right; _mockPlayerRepository.Setup(r => r.GetById(playerId)).Returns(player); _mockCollisionService.Setup(c => c.CanMoveTo(It.IsAny())).Returns(true); // Act _movementService.MovePlayer(playerId, direction); // Assert _mockPlayerRepository.Verify(r => r.Update(It.Is(p => p.Position == Vector2.Right * 5.0f)), Times.Once); } [Test] public void MovePlayer_CollisionBlocked_ShouldNotUpdatePlayerPosition() { // Arrange var playerId = "player1"; var player = new Player { Id = playerId, Position = Vector2.Zero, Speed = 5.0f }; var direction = Vector2.Right; _mockPlayerRepository.Setup(r => r.GetById(playerId)).Returns(player); _mockCollisionService.Setup(c => c.CanMoveTo(It.IsAny())).Returns(false); // Act _movementService.MovePlayer(playerId, direction); // Assert _mockPlayerRepository.Verify(r => r.Update(It.IsAny()), Times.Never); } } // ❌ 避免:难以测试的设计 public class PlayerMovementSystem : AbstractSystem { protected override void OnInit() { this.RegisterEvent(OnMovementInput); } private void OnMovementInput(MovementInputEvent e) { var player = GetModel(); // 依赖架构,难以测试 var newPosition = player.Position + e.Direction * player.Speed; if (CanMoveTo(newPosition)) // 私有方法,难以直接测试 { player.Position = newPosition; } } private bool CanMoveTo(Vector2 position) { // 复杂的碰撞检测逻辑,难以测试 return true; } } ``` ## 重构指南 ### 1. 识别代码异味 ```csharp // ❌ 代码异味:长方法、重复代码、上帝类 public class GameManager : Node { public void ProcessPlayerInput(InputEvent @event) { // 长方法 - 做太多事情 if (@event is InputEventKey keyEvent && keyEvent.Pressed) { switch (keyEvent.Keycode) { case Key.W: MovePlayer(Vector2.Up); PlayFootstepSound(); UpdatePlayerAnimation("walk_up"); CheckPlayerCollisions(); UpdateCameraPosition(); SavePlayerPosition(); break; case Key.S: MovePlayer(Vector2.Down); PlayFootstepSound(); UpdatePlayerAnimation("walk_down"); CheckPlayerCollisions(); UpdateCameraPosition(); SavePlayerPosition(); break; // 重复代码 } } } private void MovePlayer(Vector2 direction) { Player.Position += direction * Player.Speed; } private void PlayFootstepSound() { AudioPlayer.Play("footstep.wav"); } // ... 更多方法,类过于庞大 } // ✅ 重构后:职责分离 public class PlayerInputController : AbstractSystem { protected override void OnInit() { this.RegisterEvent(OnInput); } private void OnInput(InputEvent e) { if (e is InputEventKey keyEvent && keyEvent.Pressed) { var direction = GetDirectionFromKey(keyEvent.Keycode); if (direction != Vector2.Zero) { SendEvent(new PlayerMoveEvent { Direction = direction }); } } } private Vector2 GetDirectionFromKey(Key keycode) { return keycode switch { Key.W => Vector2.Up, Key.S => Vector2.Down, Key.A => Vector2.Left, Key.D => Vector2.Right, _ => Vector2.Zero }; } } public class PlayerMovementSystem : AbstractSystem { protected override void OnInit() { this.RegisterEvent(OnPlayerMove); } private void OnPlayerMove(PlayerMoveEvent e) { var playerModel = GetModel(); var newPosition = playerModel.Position + e.Direction * playerModel.Speed; if (CanMoveTo(newPosition)) { playerModel.Position = newPosition; SendEvent(new PlayerMovedEvent { NewPosition = newPosition }); } } private bool CanMoveTo(Vector2 position) { var collisionService = GetUtility(); return collisionService.CanMoveTo(position); } } public class AudioSystem : AbstractSystem { protected override void OnInit() { this.RegisterEvent(OnPlayerMoved); } private void OnPlayerMoved(PlayerMovedEvent e) { PlayFootstepSound(); } private void PlayFootstepSound() { var audioUtility = GetUtility(); audioUtility.PlaySound("footstep.wav"); } } public class AnimationSystem : AbstractSystem { protected override void OnInit() { this.RegisterEvent(OnPlayerMoved); } private void OnPlayerMoved(PlayerMovedEvent e) { var animationName = GetAnimationNameFromDirection(e.Direction); SendEvent(new PlayAnimationEvent { AnimationName = animationName }); } private string GetAnimationNameFromDirection(Vector2 direction) { if (direction == Vector2.Up) return "walk_up"; if (direction == Vector2.Down) return "walk_down"; if (direction == Vector2.Left) return "walk_left"; if (direction == Vector2.Right) return "walk_right"; return "idle"; } } ``` ### 2. 渐进式重构 ```csharp // 第一步:提取重复代码 public class PlayerController : Node { public void ProcessInput(InputEvent @event) { if (@event is InputEventKey keyEvent && keyEvent.Pressed) { Vector2 direction; switch (keyEvent.Keycode) { case Key.W: direction = Vector2.Up; break; case Key.S: direction = Vector2.Down; break; case Key.A: direction = Vector2.Left; break; case Key.D: direction = Vector2.Right; break; default: return; } MovePlayer(direction); } } } // 第二步:提取方法 public class PlayerController : Node { public void ProcessInput(InputEvent @event) { if (@event is InputEventKey keyEvent && keyEvent.Pressed) { var direction = GetDirectionFromKey(keyEvent.Keycode); if (direction != Vector2.Zero) { MovePlayer(direction); } } } private Vector2 GetDirectionFromKey(Key keycode) { return keycode switch { Key.W => Vector2.Up, Key.S => Vector2.Down, Key.A => Vector2.Left, Key.D => Vector2.Right, _ => Vector2.Zero }; } } // 第三步:引入系统和事件 public class PlayerController : AbstractSystem { protected override void OnInit() { this.RegisterEvent(OnInput); } private void OnInput(InputEvent e) { if (e is InputEventKey keyEvent && keyEvent.Pressed) { var direction = GetDirectionFromKey(keyEvent.Keycode); if (direction != Vector2.Zero) { SendEvent(new PlayerMoveEvent { Direction = direction }); } } } private Vector2 GetDirectionFromKey(Key keycode) { return keycode switch { Key.W => Vector2.Up, Key.S => Vector2.Down, Key.A => Vector2.Left, Key.D => Vector2.Right, _ => Vector2.Zero }; } } ``` --- ## 模式选择与组合 ### 何时使用哪种模式? #### 小型项目(原型、Demo) ```csharp // 推荐组合:MVC + 事件驱动 + 服务定位器 public class SimpleGameArchitecture : Architecture { protected override void Init() { // Model RegisterModel(new PlayerModel()); RegisterModel(new GameModel()); // System RegisterSystem(new GameplaySystem()); RegisterSystem(new AudioSystem()); // 使用服务定位器模式 // Controller 通过 this.GetModel/GetSystem 获取服务 } } ``` **优势**: - 快速开发 - 代码简洁 - 易于理解 #### 中型项目(独立游戏) ```csharp // 推荐组合:MVC + MVVM + 命令/查询 + 事件驱动 + 依赖注入 public class GameArchitecture : Architecture { protected override void Init() { // 注册 Utility(依赖注入) RegisterUtility<IStorageService>(new LocalStorageService()); RegisterUtility<IAudioService>(new GodotAudioService()); // 注册 Model(MVVM) RegisterModel(new PlayerModel()); RegisterModel(new PlayerViewModel()); // 注册 System(命令/查询处理) RegisterSystem(new CombatSystem()); RegisterSystem(new InventorySystem()); // 状态机 var stateMachine = new StateMachineSystem(); stateMachine .Register(new MenuState()) .Register(new GameplayState()); RegisterSystem<IStateMachineSystem>(stateMachine); } } ``` **优势**: - 职责清晰 - 易于测试 - 支持团队协作 #### 大型项目(商业游戏) ```csharp // 推荐组合:所有模式 + 模块化架构 public class GameArchitecture : Architecture { protected override void Init() { // 模块化安装 InstallModule(new CoreModule()); InstallModule(new AudioModule()); InstallModule(new NetworkModule()); InstallModule(new UIModule()); InstallModule(new GameplayModule()); } } // 核心模块 public class CoreModule : IArchitectureModule { public void Install(IArchitecture architecture) { // 依赖注入 architecture.RegisterUtility<IStorageService>(new CloudStorageService()); architecture.RegisterUtility<IAnalyticsService>(new AnalyticsService()); // 对象池 architecture.RegisterSystem(new BulletPoolSystem()); architecture.RegisterSystem(new ParticlePoolSystem()); } } // 游戏玩法模块 public class GameplayModule : IArchitectureModule { public void Install(IArchitecture architecture) { // Model architecture.RegisterModel(new PlayerModel()); architecture.RegisterModel(new EnemyModel()); // System(使用命令/查询模式) architecture.RegisterSystem(new CombatSystem()); architecture.RegisterSystem(new MovementSystem()); // 状态机 var stateMachine = new StateMachineSystem(); stateMachine .Register(new MenuState()) .Register(new LoadingState()) .Register(new GameplayState()) .Register(new PauseState()) .Register(new GameOverState()); architecture.RegisterSystem<IStateMachineSystem>(stateMachine); } } ``` **优势**: - 高度模块化 - 易于维护和扩展 - 支持大型团队 - 完善的测试覆盖 ### 模式组合示例 #### 组合 1:MVVM + 命令模式 ```csharp // ViewModel public class ShopViewModel : AbstractModel { public BindableProperty<int> PlayerGold { get; } = new(1000); public BindableProperty<bool> CanBuy { get; } = new(true); public BindableProperty<string> StatusMessage { get; } = new(""); protected override void OnInit() { // 监听购买事件 this.RegisterEvent<ItemPurchasedEvent>(OnItemPurchased); this.RegisterEvent<InsufficientGoldEvent>(OnInsufficientGold); // 监听金币变化 PlayerGold.Register(gold => { CanBuy.Value = gold >= 100; }); } private void OnItemPurchased(ItemPurchasedEvent e) { PlayerGold.Value -= e.Cost; StatusMessage.Value = $"购买成功:{e.ItemName}"; } private void OnInsufficientGold(InsufficientGoldEvent e) { StatusMessage.Value = "金币不足!"; } } // View public class ShopView : Control { private ShopViewModel _viewModel; public override void _Ready() { _viewModel = GetModel<ShopViewModel>(); // 数据绑定 _viewModel.PlayerGold.Register(gold => { GetNode<Label>("GoldLabel").Text = $"金币:{gold}"; }); _viewModel.CanBuy.Register(canBuy => { GetNode<Button>("BuyButton").Disabled = !canBuy; }); _viewModel.StatusMessage.Register(msg => { GetNode<Label>("StatusLabel").Text = msg; }); } private void OnBuyButtonPressed() { // 发送命令 this.SendCommand(new BuyItemCommand { Input = new BuyItemInput { ItemId = "sword_01" } }); } } ``` #### 组合 2:状态模式 + 对象池 ```csharp // 游戏状态使用对象池 public class GameplayState : ContextAwareStateBase { private BulletPoolSystem _bulletPool; private ParticlePoolSystem _particlePool; public override void OnEnter(IState from) { // 获取对象池 _bulletPool = this.GetSystem<BulletPoolSystem>(); _particlePool = this.GetSystem<ParticlePoolSystem>(); // 预热对象池 _bulletPool.PrewarmPool(100); _particlePool.PrewarmPool(200); // 注册事件 this.RegisterEvent<FireWeaponEvent>(OnFireWeapon); } private void OnFireWeapon(FireWeaponEvent e) { // 从池中获取子弹 var bullet = _bulletPool.Spawn(); bullet.Position = e.Position; bullet.Direction = e.Direction; // 生成粒子效果 var particle = _particlePool.Spawn(); particle.Position = e.Position; } public override void OnExit(IState to) { // 回收所有对象 _bulletPool.RecycleAll(); _particlePool.RecycleAll(); } } ``` #### 组合 3:事件驱动 + 查询模式 ```csharp // 成就系统:监听事件,使用查询验证条件 public class AchievementSystem : AbstractSystem { protected override void OnInit() { this.RegisterEvent<EnemyKilledEvent>(OnEnemyKilled); this.RegisterEvent<LevelCompletedEvent>(OnLevelCompleted); } private void OnEnemyKilled(EnemyKilledEvent e) { // 查询击杀总数 var query = new GetTotalKillsQuery { Input = new EmptyQueryInput() }; var totalKills = this.SendQuery(query); // 检查成就 if (totalKills == 100) { UnlockAchievement("kill_100_enemies"); } } private void OnLevelCompleted(LevelCompletedEvent e) { // 查询是否满足完美通关条件 var query = new IsPerfectClearQuery { Input = new IsPerfectClearInput { LevelId = e.LevelId } }; var isPerfect = this.SendQuery(query); if (isPerfect) { UnlockAchievement($"perfect_clear_level_{e.LevelId}"); } } } ``` ### 模式选择决策树 ``` 需要管理游戏状态? ├─ 是 → 使用状态模式 └─ 否 → 继续 需要频繁创建/销毁对象? ├─ 是 → 使用对象池模式 └─ 否 → 继续 需要解耦组件通信? ├─ 是 → 使用事件驱动模式 └─ 否 → 继续 需要封装操作? ├─ 是 → 使用命令模式 └─ 否 → 继续 需要分离读写操作? ├─ 是 → 使用查询模式(CQRS) └─ 否 → 继续 需要数据绑定和响应式 UI? ├─ 是 → 使用 MVVM 模式 └─ 否 → 使用 MVC 模式 需要管理依赖? ├─ 大型项目 → 使用依赖注入 └─ 小型项目 → 使用服务定位器 ``` ## 常见问题 ### Q1: 应该使用 MVC 还是 MVVM? **A**: 取决于项目需求: - **使用 MVC**: - 简单的 UI 更新 - 不需要复杂的数据绑定 - 快速原型开发 - **使用 MVVM**: - 复杂的数据驱动 UI - 需要自动更新界面 - 大量计算属性 **推荐**:可以混合使用,简单界面用 MVC,复杂界面用 MVVM。 ### Q2: 命令模式和查询模式有什么区别? **A**: | 特性 | 命令模式 | 查询模式 | |---------|----------------|---------------------| | **目的** | 修改状态 | 读取数据 | | **返回值** | 可选 | 必须有 | | **副作用** | 有 | 无 | | **示例** | BuyItemCommand | GetPlayerStatsQuery | **原则**:命令改变状态,查询读取状态,两者不混用。 ### Q3: 何时使用事件,何时使用命令? **A**: - **使用事件**: - 通知状态变化 - 一对多通信 - 跨模块通信 - 不关心处理结果 - **使用命令**: - 执行具体操作 - 需要封装逻辑 - 需要撤销/重做 - 需要返回结果 **示例**: ```csharp // 使用命令执行操作 this.SendCommand(new BuyItemCommand()); // 使用事件通知结果 this.SendEvent(new ItemPurchasedEvent()); ``` ### Q4: 依赖注入和服务定位器哪个更好? **A**: - **依赖注入**: - ✅ 依赖关系明确 - ✅ 易于测试 - ✅ 编译时检查 - ❌ 配置复杂 - **服务定位器**: - ✅ 简单直接 - ✅ 易于使用 - ❌ 依赖隐式 - ❌ 难以测试 **推荐**: - 小项目:服务定位器 - 大项目:依赖注入 - 混合使用:核心服务用依赖注入,辅助服务用服务定位器 ### Q5: 对象池适合哪些场景? **A**: **适合**: - 频繁创建/销毁的对象(子弹、粒子) - 创建成本高的对象(网络连接) - 需要稳定帧率的场景 **不适合**: - 创建频率低的对象 - 对象状态复杂难以重置 - 内存受限的场景 **示例**: ```csharp // ✅ 适合使用对象池 - 子弹、导弹 - 粒子效果 - UI 元素(列表项) - 音效播放器 // ❌ 不适合使用对象池 - 玩家角色 - 关卡数据 - 配置对象 ``` ### Q6: 状态机和简单的 if-else 有什么区别? **A**: **简单 if-else**: ```csharp // ❌ 难以维护 public void Update() { if (gameState == GameState.Menu) { UpdateMenu(); } else if (gameState == GameState.Playing) { UpdateGameplay(); } else if (gameState == GameState.Paused) { UpdatePause(); } // 状态逻辑分散,难以管理 } ``` **状态机**: ```csharp // ✅ 清晰易维护 public class MenuState : ContextAwareStateBase { public override void OnEnter(IState from) { // 进入菜单的所有逻辑集中在这里 } public override void OnExit(IState to) { // 退出菜单的所有逻辑集中在这里 } } ``` **优势**: - 状态逻辑封装 - 易于添加新状态 - 支持状态转换验证 - 支持状态历史 ### Q7: 如何避免过度设计? **A**: **原则**: 1. **从简单开始**:先用最简单的方案 2. **按需重构**:遇到问题再优化 3. **YAGNI 原则**:You Aren't Gonna Need It **示例**: ```csharp // 第一版:简单直接 public class Player { public int Health = 100; } // 第二版:需要通知时添加事件 public class Player { private int _health = 100; public int Health { get => _health; set { _health = value; OnHealthChanged?.Invoke(value); } } public event Action<int> OnHealthChanged; } // 第三版:需要更多功能时使用 BindableProperty public class PlayerModel : AbstractModel { public BindableProperty<int> Health { get; } = new(100); } ``` ### Q8: 如何在现有项目中引入这些模式? **A**: **渐进式重构**: 1. **第一步:引入事件系统** ```csharp // 替换直接调用为事件 // 之前:uiManager.UpdateHealth(health); // 之后:SendEvent(new HealthChangedEvent { Health = health }); ``` 2. **第二步:提取 Model** ```csharp // 将数据从各处集中到 Model public class PlayerModel : AbstractModel { public BindableProperty<int> Health { get; } = new(100); } ``` 3. **第三步:引入命令模式** ```csharp // 封装操作为命令 public class HealPlayerCommand : AbstractCommand { protected override void OnExecute() { var player = this.GetModel<PlayerModel>(); player.Health.Value = player.MaxHealth.Value; } } ``` 4. **第四步:添加查询模式** ```csharp // 分离读操作 public class GetPlayerStatsQuery : AbstractQuery<PlayerStats> { protected override PlayerStats OnDo() { // 查询逻辑 } } ``` ### Q9: 性能会受到影响吗? **A**: **影响很小**: - 事件系统:微秒级开销 - 命令/查询:几乎无开销 - IoC 容器:字典查找,O(1) **优化建议**: 1. **避免频繁事件**:不要每帧发送事件 2. **缓存查询结果**:复杂查询结果可以缓存 3. **使用对象池**:减少 GC 压力 4. **批量操作**:合并多个小操作 **性能对比**: ```csharp // 直接调用:~1ns player.Health = 100; // 通过命令:~100ns SendCommand(new SetHealthCommand { Health = 100 }); // 差异可以忽略不计,但带来了更好的架构 ``` ### Q10: 如何测试使用这些模式的代码? **A**: **单元测试示例**: ```csharp [Test] public void BuyItemCommand_InsufficientGold_ShouldNotBuyItem() { // Arrange var architecture = new TestArchitecture(); var playerModel = new PlayerModel(); playerModel.Gold.Value = 50; // 金币不足 architecture.RegisterModel(playerModel); var command = new BuyItemCommand { Input = new BuyItemInput { ItemId = "sword", Price = 100 } }; command.SetArchitecture(architecture); // Act command.Execute(); // Assert Assert.AreEqual(50, playerModel.Gold.Value); // 金币未变化 } [Test] public void GetPlayerStatsQuery_ShouldReturnCorrectStats() { // Arrange var architecture = new TestArchitecture(); var playerModel = new PlayerModel(); playerModel.Level.Value = 10; playerModel.Health.Value = 80; architecture.RegisterModel(playerModel); var query = new GetPlayerStatsQuery(); query.SetArchitecture(architecture); // Act var stats = query.Do(); // Assert Assert.AreEqual(10, stats.Level); Assert.AreEqual(80, stats.Health); } ``` --- ## 总结 遵循这些架构模式最佳实践,你将能够构建: - ✅ **清晰的代码结构** - 易于理解和维护 - ✅ **松耦合的组件** - 便于测试和扩展 - ✅ **可重用的模块** - 提高开发效率 - ✅ **健壮的错误处理** - 提高系统稳定性 - ✅ **完善的测试覆盖** - 保证代码质量 ### 关键要点 1. **从简单开始**:不要过度设计,按需添加模式 2. **理解每个模式的适用场景**:选择合适的模式解决问题 3. **模式可以组合使用**:发挥各自优势 4. **持续重构**:随着项目发展优化架构 5. **注重可测试性**:好的架构应该易于测试 ### 推荐学习路径 1. **入门**:MVC + 事件驱动 2. **进阶**:命令模式 + 查询模式 + MVVM 3. **高级**:状态模式 + 对象池 + 依赖注入 4. **专家**:模块化架构 + 所有模式组合 ### 相关资源 - [架构核心文档](/zh-CN/core/architecture) - [命令模式文档](/zh-CN/core/command) - [查询模式文档](/zh-CN/core/query) - [事件系统文档](/zh-CN/core/events) - [状态机文档](/zh-CN/core/state-machine) - [IoC 容器文档](/zh-CN/core/ioc) 记住,好的架构不是一蹴而就的,需要持续的学习、实践和改进。 --- **文档版本**: 2.0.0 **最后更新**: 2026-03-07 **作者**: GFramework Team