GFramework/docs/zh-CN/best-practices/architecture-patterns.md
GeWuYou fb14d7122c docs(style): 更新文档中的命名空间导入格式
- 将所有小写的命名空间导入更正为首字母大写格式
- 统一 GFramework 框架的命名空间引用规范
- 修复 core、ecs、godot 等模块的命名空间导入错误
- 标准化文档示例代码中的 using 语句格式
- 确保所有文档中的命名空间引用保持一致性
- 更新 global using 语句以匹配正确的命名空间格式
2026-03-10 07:18:49 +08:00

92 KiB
Raw Permalink Blame History

架构设计模式指南

全面介绍 GFramework 中的架构设计模式,帮助你构建清晰、可维护、可扩展的游戏架构。

📋 目录

概述

架构设计模式是经过验证的解决方案用于解决软件开发中的常见问题。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 模式

概念

MVCModel-View-Controller是一种将应用程序分为三个核心组件的架构模式

  • Model模型:管理数据和业务逻辑
  • View视图:显示数据给用户
  • Controller控制器:处理用户输入,协调 Model 和 View

在 GFramework 中的实现

// 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 模式

概念

MVVMModel-View-ViewModel是 MVC 的变体,强调数据绑定和响应式编程:

  • Model:数据和业务逻辑
  • View:用户界面
  • ViewModelView 的抽象,提供数据绑定

在 GFramework 中的实现

// 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 中的实现

// 定义命令输入
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 });
    }
}

支持撤销的命令

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 中的实现

// 定义查询输入
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}");
    }
}

复杂查询示例

// 查询背包中可装备的物品
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 中的实现

// 定义事件
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<AchievementUnlockedEvent>(OnAchievementUnlocked)
            .AddToUnregisterList(_unregisterList);

        // 监听玩家死亡事件
        this.RegisterEvent<PlayerDiedEvent>(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();
    }
}

事件组合

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 中的实现

// 定义接口
public interface IStorageService
{
    Task SaveAsync&lt;T&gt;(string key, T data);
    Task&lt;T&gt; LoadAsync&lt;T&gt;(string key);
}

public interface IAudioService
{
    void PlaySound(string soundId);
    void PlayMusic(string musicId);
}

// 实现服务
public class LocalStorageService : IStorageService
{
    public async Task SaveAsync&lt;T&gt;(string key, T data)
    {
        var json = JsonSerializer.Serialize(data);
        await File.WriteAllTextAsync($"saves/{key}.json", json);
    }

    public async Task&lt;T&gt; LoadAsync&lt;T&gt;(string key)
    {
        var json = await File.ReadAllTextAsync($"saves/{key}.json");
        return JsonSerializer.Deserialize&lt;T&gt;(json);
    }
}

public class GodotAudioService : IAudioService
{
    private AudioStreamPlayer _soundPlayer;
    private AudioStreamPlayer _musicPlayer;

    public void PlaySound(string soundId)
    {
        var sound = GD.Load&lt;AudioStream&gt;($"res://sounds/{soundId}.ogg");
        _soundPlayer.Stream = sound;
        _soundPlayer.Play();
    }

    public void PlayMusic(string musicId)
    {
        var music = GD.Load&lt;AudioStream&gt;($"res://music/{musicId}.ogg");
        _musicPlayer.Stream = music;
        _musicPlayer.Play();
    }
}

// 在架构中注册服务
public class GameArchitecture : Architecture
{
    protected override void Init()
    {
        // 注册服务实现
        RegisterUtility&lt;IStorageService&gt;(new LocalStorageService());
        RegisterUtility&lt;IAudioService&gt;(new GodotAudioService());

        // 注册 SystemSystem 会自动获取依赖)
        RegisterSystem(new SaveSystem());
        RegisterSystem(new AudioSystem());
    }
}

// System 使用依赖注入
public class SaveSystem : AbstractSystem
{
    private IStorageService _storageService;

    protected override void OnInit()
    {
        // 从容器获取依赖
        _storageService = this.GetUtility&lt;IStorageService&gt;();

        this.RegisterEvent&lt;SaveGameEvent&gt;(OnSaveGame);
    }

    private async void OnSaveGame(SaveGameEvent e)
    {
        var playerModel = this.GetModel&lt;PlayerModel&gt;();
        var saveData = new SaveData
        {
            PlayerName = playerModel.Name.Value,
            Level = playerModel.Level.Value,
            Health = playerModel.Health.Value
        };

        await _storageService.SaveAsync("current_save", saveData);
    }
}

构造函数注入(推荐)

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&lt;SaveGameEvent&gt;(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&lt;IStorageService&gt;(storageService);
        RegisterUtility&lt;IAudioService&gt;(audioService);

        // 构造函数注入
        RegisterSystem(new SaveSystem(storageService, audioService));
    }
}

依赖注入优势

  • 易于测试:可以注入模拟对象
  • 松耦合:依赖接口而非实现
  • 灵活配置:运行时选择实现
  • 提高可维护性:依赖关系清晰

最佳实践

  1. 依赖接口而非实现:使用 IStorageService 而非 LocalStorageService
  2. 优先使用构造函数注入:依赖关系更明确
  3. 避免循环依赖System 不应相互依赖
  4. 使用 IoC 容器管理生命周期:让框架管理对象创建

服务定位器模式

概念

服务定位器模式提供一个全局访问点来获取服务,是依赖注入的替代方案。

在 GFramework 中的实现

// GFramework 的 Architecture 本身就是服务定位器
public class GameplaySystem : AbstractSystem
{
    protected override void OnInit()
    {
        // 通过服务定位器获取服务
        var playerModel = this.GetModel&lt;PlayerModel&gt;();
        var audioSystem = this.GetSystem&lt;AudioSystem&gt;();
        var storageUtility = this.GetUtility&lt;IStorageUtility&gt;();

        // 使用服务
        playerModel.Health.Value = 100;
        audioSystem.PlayBGM("gameplay");
    }
}

// 在 Controller 中使用
[ContextAware]
public partial class MenuController : IController
{
    public void OnStartButtonClicked()
    {
        // 通过架构获取服务
        var gameModel = this.GetModel<GameModel>();
        gameModel.GameState.Value = GameState.Playing;

        // 发送命令
        this.SendCommand(new StartGameCommand());
    }
}

自定义服务定位器

// 创建专门的服务定位器
public static class ServiceLocator
{
    private static readonly Dictionary&lt;Type, object&gt; _services = new();

    public static void Register&lt;T&gt;(T service)
    {
        _services[typeof(T)] = service;
    }

    public static T Get&lt;T&gt;()
    {
        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&lt;IAnalyticsService&gt;(new AnalyticsService());
        ServiceLocator.Register&lt;ILeaderboardService&gt;(new LeaderboardService());
    }
}

public class GameOverScreen
{
    public void SubmitScore(int score)
    {
        // 获取服务
        var leaderboard = ServiceLocator.Get&lt;ILeaderboardService&gt;();
        leaderboard.SubmitScore(score);

        var analytics = ServiceLocator.Get&lt;IAnalyticsService&gt;();
        analytics.TrackEvent("game_over", new { score });
    }
}

服务定位器 vs 依赖注入

特性 服务定位器 依赖注入
依赖可见性 隐式(运行时获取) 显式(构造函数参数)
易用性 简单直接 需要配置
测试性 较难(需要模拟全局状态) 容易(注入模拟对象)
编译时检查
适用场景 快速原型、小项目 大型项目、团队协作

最佳实践

  1. 小项目使用服务定位器:简单直接
  2. 大项目使用依赖注入:更易维护
  3. 避免过度使用:不要把所有东西都放入定位器
  4. 提供清晰的 APIGetModel、GetSystem、GetUtility

对象池模式

概念

对象池模式通过复用对象来减少内存分配和垃圾回收,提高性能。

在 GFramework 中的实现

// 定义可池化对象
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&lt;Bullet&gt;
{
    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&lt;BulletPoolSystem&gt;();
        this.RegisterEvent&lt;FireWeaponEvent&gt;(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);
    }
}

通用对象池

// 通用对象池实现
public class ObjectPool&lt;T&gt; where T : class, new()
{
    private readonly Stack&lt;T&gt; _pool = new();
    private readonly Action&lt;T&gt; _onSpawn;
    private readonly Action&lt;T&gt; _onRecycle;
    private readonly int _maxSize;

    public ObjectPool(int initialSize = 10, int maxSize = 100,
        Action&lt;T&gt; onSpawn = null, Action&lt;T&gt; onRecycle = null)
    {
        _maxSize = maxSize;
        _onSpawn = onSpawn;
        _onRecycle = onRecycle;

        // 预创建对象
        for (int i = 0; i &lt; initialSize; i++)
        {
            _pool.Push(new T());
        }
    }

    public T Spawn()
    {
        T obj = _pool.Count &gt; 0 ? _pool.Pop() : new T();
        _onSpawn?.Invoke(obj);
        return obj;
    }

    public void Recycle(T obj)
    {
        if (_pool.Count &lt; _maxSize)
        {
            _onRecycle?.Invoke(obj);
            _pool.Push(obj);
        }
    }

    public void Clear()
    {
        _pool.Clear();
    }
}

// 使用通用对象池
public class ParticleSystem : AbstractSystem
{
    private ObjectPool&lt;Particle&gt; _particlePool;

    protected override void OnInit()
    {
        _particlePool = new ObjectPool&lt;Particle&gt;(
            initialSize: 100,
            maxSize: 500,
            onSpawn: p => p.Reset(),
            onRecycle: p => p.Clear()
        );
    }

    public void EmitParticles(Vector3 position, int count)
    {
        for (int i = 0; i &lt; count; i++)
        {
            var particle = _particlePool.Spawn();
            particle.Position = position;
            particle.Velocity = GetRandomVelocity();
        }
    }
}

对象池优势

  • 减少 GC 压力:复用对象,减少内存分配
  • 提高性能:避免频繁创建销毁对象
  • 稳定帧率:减少 GC 导致的卡顿
  • 适合高频对象:子弹、粒子、特效等

最佳实践

  1. 预热池:提前创建对象避免运行时分配
  2. 设置最大容量:防止池无限增长
  3. 重置对象状态OnRecycle 中清理状态
  4. 监控池使用情况:记录 Spawn/Recycle 次数

状态模式

概念

状态模式允许对象在内部状态改变时改变其行为,将状态相关的行为封装到独立的状态类中。

在 GFramework 中的实现

// 定义游戏状态
public class MenuState : ContextAwareStateBase
{
    public override void OnEnter(IState from)
    {
        Console.WriteLine("进入菜单状态");

        // 显示菜单 UI
        var uiSystem = this.GetSystem&lt;UISystem&gt;();
        uiSystem.ShowMenu();

        // 播放菜单音乐
        var audioSystem = this.GetSystem&lt;AudioSystem&gt;();
        audioSystem.PlayBGM("menu_theme");
    }

    public override void OnExit(IState to)
    {
        Console.WriteLine("退出菜单状态");

        // 隐藏菜单 UI
        var uiSystem = this.GetSystem&lt;UISystem&gt;();
        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&lt;GameModel&gt;();
        gameModel.Reset();

        // 加载关卡
        this.SendCommand(new LoadLevelCommand { LevelId = 1 });

        // 播放游戏音乐
        var audioSystem = this.GetSystem&lt;AudioSystem&gt;();
        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&lt;TimeSystem&gt;();
        timeSystem.Pause();

        // 显示暂停菜单
        var uiSystem = this.GetSystem&lt;UISystem&gt;();
        uiSystem.ShowPauseMenu();
    }

    public override void OnExit(IState to)
    {
        Console.WriteLine("退出暂停状态");

        // 恢复游戏
        var timeSystem = this.GetSystem&lt;TimeSystem&gt;();
        timeSystem.Resume();

        // 隐藏暂停菜单
        var uiSystem = this.GetSystem&lt;UISystem&gt;();
        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&lt;IStateMachineSystem&gt;(stateMachine);
    }
}

// 使用状态机
[ContextAware]
public partial class GameController : IController
{
    public async Task StartGame()
    {
        var stateMachine = this.GetSystem<IStateMachineSystem>();
        await stateMachine.ChangeToAsync<GameplayState>();
    }

    public async Task PauseGame()
    {
        var stateMachine = this.GetSystem<IStateMachineSystem>();
        await stateMachine.ChangeToAsync<PauseState>();
    }

    public async Task ResumeGame()
    {
        var stateMachine = this.GetSystem<IStateMachineSystem>();
        await stateMachine.ChangeToAsync<GameplayState>();
    }
}

异步状态

public class LoadingState : AsyncContextAwareStateBase
{
    public override async Task OnEnterAsync(IState from)
    {
        Console.WriteLine("开始加载...");

        // 显示加载界面
        var uiSystem = this.GetSystem&lt;UISystem&gt;();
        uiSystem.ShowLoadingScreen();

        // 异步加载资源
        await LoadResourcesAsync();

        Console.WriteLine("加载完成");

        // 自动切换到游戏状态
        var stateMachine = this.GetSystem&lt;IStateMachineSystem&gt;();
        await stateMachine.ChangeToAsync&lt;GameplayState&gt;();
    }

    private async Task LoadResourcesAsync()
    {
        // 模拟异步加载
        await Task.Delay(2000);
    }

    public override async Task OnExitAsync(IState to)
    {
        // 隐藏加载界面
        var uiSystem = this.GetSystem&lt;UISystem&gt;();
        uiSystem.HideLoadingScreen();

        await Task.CompletedTask;
    }
}

状态模式优势

  • 清晰的状态管理:每个状态独立封装
  • 易于扩展:添加新状态不影响现有代码
  • 状态转换验证CanTransitionTo 控制合法转换
  • 支持异步操作:异步状态处理加载等操作

最佳实践

  1. 状态保持单一职责:每个状态只负责一个场景
  2. 使用转换验证:防止非法状态转换
  3. 在 OnEnter 初始化OnExit 清理:保持状态独立
  4. 异步操作使用异步状态:避免阻塞主线程

设计原则

1. 单一职责原则 (SRP)

确保每个类只负责一个功能领域:

// ✅ 好的做法:职责单一
public class PlayerMovementController : AbstractSystem
{
    protected override void OnInit()
    {
        this.RegisterEvent<PlayerInputEvent>(OnPlayerInput);
    }
    
    private void OnPlayerInput(PlayerInputEvent e)
    {
        // 只负责移动逻辑
        ProcessMovement(e.Direction);
    }
    
    private void ProcessMovement(Vector2 direction)
    {
        // 移动相关的业务逻辑
    }
}

public class PlayerCombatController : AbstractSystem
{
    protected override void OnInit()
    {
        this.RegisterEvent<AttackInputEvent>(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)

设计应该对扩展开放,对修改封闭:

// ✅ 好的做法:使用接口和策略模式
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<WeaponType, IWeaponStrategy> _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)

高层模块不应该依赖低层模块,两者都应该依赖抽象:

// ✅ 好的做法:依赖抽象
public interface IDataStorage
{
    Task SaveAsync&lt;T&gt;(string key, T data);
    Task&lt;T&gt; LoadAsync&lt;T&gt;(string key, T defaultValue = default);
    Task<bool> ExistsAsync(string key);
}

public class FileStorage : IDataStorage
{
    public async Task SaveAsync&lt;T&gt;(string key, T data)
    {
        var json = JsonConvert.SerializeObject(data);
        await File.WriteAllTextAsync(GetFilePath(key), json);
    }
    
    public async Task&lt;T&gt; LoadAsync&lt;T&gt;(string key, T defaultValue = default)
    {
        var filePath = GetFilePath(key);
        if (!File.Exists(filePath))
            return defaultValue;
            
        var json = await File.ReadAllTextAsync(filePath);
        return JsonConvert.DeserializeObject&lt;T&gt;(json);
    }
    
    public async Task<bool> 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&lt;T&gt;(string key, T data)
    {
        // 云存储实现
        await UploadToCloud(key, data);
    }
    
    public async Task&lt;T&gt; LoadAsync&lt;T&gt;(string key, T defaultValue = default)
    {
        // 云存储实现
        return await DownloadFromCloud&lt;T&gt;(key, defaultValue);
    }
    
    public async Task<bool> 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<SaveData> LoadGameAsync()
    {
        return await _storage.LoadAsync<SaveData>("current_save");
    }
}

// ❌ 避免:依赖具体实现
public class SaveSystem : AbstractSystem
{
    private readonly FileStorage _storage; // 直接依赖具体实现
    
    public SaveSystem()
    {
        _storage = new FileStorage(); // 硬编码依赖
    }
    
    // 无法轻松切换到其他存储方式
}

架构分层

1. 清晰的层次结构

// ✅ 好的做法:清晰的分层架构
namespace Game.Models
{
    // 数据层:只负责存储状态
    public class PlayerModel : AbstractModel
    {
        public BindableProperty<int> Health { get; } = new(100);
        public BindableProperty<int> MaxHealth { get; } = new(100);
        public BindableProperty<Vector2> Position { get; } = new(Vector2.Zero);
        public BindableProperty<PlayerState> 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<PlayerModel>();
            _gameModel = GetModel<GameModel>();
            
            this.RegisterEvent<PlayerInputEvent>(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<PlayerModel>();
            
            // 监听用户输入
            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<ProgressBar>("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. 避免层次混乱

// ❌ 避免:层次混乱
public class PlayerController : Node, IController
{
    // 混合了数据层、业务逻辑层和控制层的职责
    public BindableProperty<int> 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. 构造函数注入

// ✅ 好的做法:构造函数注入
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<AttackEvent>(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<PlayerModel>();
        _weaponService = GetService<IWeaponService>();
        _soundService = GetService<ISoundService>();
        _effectService = GetService<IEffectService>();
    }
    
    // 测试时难以模拟依赖
}

2. 接口隔离

// ✅ 好的做法:小而专注的接口
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. 事件命名和结构

// ✅ 好的做法:清晰的事件命名和结构
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<string, object> Properties { get; } // 难以理解
}

2. 事件处理职责

// ✅ 好的做法:单一职责的事件处理
public class UIHealthBarController : AbstractSystem
{
    protected override void OnInit()
    {
        this.RegisterEvent<PlayerHealthChangedEvent>(OnPlayerHealthChanged);
        this.RegisterEvent<PlayerDiedEvent>(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<PlayerHealthChangedEvent>(OnPlayerHealthChanged);
        this.RegisterEvent<PlayerDiedEvent>(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<PlayerHealthChangedEvent>(OnPlayerHealthChanged);
        this.RegisterEvent<PlayerDiedEvent>(OnPlayerDied);
        this.RegisterEvent<WeaponEquippedEvent>(OnWeaponEquipped);
        this.RegisterEvent<LevelUpEvent>(OnLevelUp);
        // 注册太多事件,职责混乱
    }
    
    private void OnPlayerHealthChanged(PlayerHealthChangedEvent e)
    {
        UpdateUI();          // UI职责
        PlayAudio();         // 音频职责
        SaveStatistics();     // 存档职责
        UpdateAchievements(); // 成就系统职责
        // 一个事件处理器承担太多职责
    }
}

模块化架构

1. 模块边界清晰

// ✅ 好的做法:清晰的模块边界
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. 模块间通信

// ✅ 好的做法:通过事件进行模块间通信
public class AudioModule : AbstractModule
{
    public override void Install(IArchitecture architecture)
    {
        architecture.RegisterSystem(new AudioSystem());
    }
}

public class AudioSystem : AbstractSystem
{
    protected override void OnInit()
    {
        // 监听其他模块发送的事件
        this.RegisterEvent<PlayerAttackEvent>(OnPlayerAttack);
        this.RegisterEvent<PlayerDiedEvent>(OnPlayerDied);
        this.RegisterEvent<WeaponEquippedEvent>(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<AttackInputEvent>(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<AudioSystem>();
    }
    
    private void OnAttackInput(AttackInputEvent e)
    {
        ProcessAttack(e);
        
        // 直接调用其他模块的方法
        _audioSystem.PlayAttackSound(weaponType);
    }
}

错误处理策略

1. 异常处理层次

// ✅ 好的做法:分层异常处理
public class GameApplicationException : Exception
{
    public string ErrorCode { get; }
    public Dictionary<string, object> Context { get; }
    
    public GameApplicationException(string message, string errorCode, 
        Dictionary<string, object> context = null, Exception innerException = null)
        : base(message, innerException)
    {
        ErrorCode = errorCode;
        Context = context ?? new Dictionary<string, object>();
    }
}

public class PlayerException : GameApplicationException
{
    public PlayerException(string message, string errorCode, 
        Dictionary<string, object> context = null, Exception innerException = null)
        : base(message, errorCode, context, innerException)
    {
    }
}

public class InventoryException : GameApplicationException
{
    public InventoryException(string message, string errorCode, 
        Dictionary<string, object> 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<string, object>
                {
                    ["playerId"] = playerId,
                    ["itemId"] = item.Id,
                    ["validationError"] = ex.Message
                },
                ex
            );
        }
        catch (InventoryFullException ex)
        {
            throw new InventoryException(
                $"Player {playerId} inventory is full",
                "INVENTORY_FULL",
                new Dictionary<string, object>
                {
                    ["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<string, object>
                {
                    ["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. 错误恢复策略

// ✅ 好的做法:优雅的错误恢复
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<SaveData> LoadSaveDataAsync(string saveId)
    {
        try
        {
            // 尝试从主存储加载
            return await _primaryStorage.ReadAsync<SaveData>(saveId);
        }
        catch (StorageException ex)
        {
            Logger.Warning($"Failed to load from primary storage: {ex.Message}");
            
            try
            {
                // 尝试从备份存储加载
                var backupData = await _backupStorage.ReadAsync<SaveData>(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<SaveData> LoadSaveDataAsync(string saveId)
    {
        try
        {
            return await _storage.ReadAsync<SaveData>(saveId);
        }
        catch (Exception ex)
        {
            // 直接抛出异常,不提供恢复机制
            throw new Exception($"Failed to load save: {ex.Message}", ex);
        }
    }
}

测试策略

1. 可测试的架构设计

// ✅ 好的做法:可测试的架构
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<IPlayerRepository> _mockPlayerRepository;
    private Mock<ICollisionService> _mockCollisionService;
    private Mock<IMapService> _mockMapService;
    private PlayerMovementService _movementService;
    
    [SetUp]
    public void Setup()
    {
        _mockPlayerRepository = new Mock<IPlayerRepository>();
        _mockCollisionService = new Mock<ICollisionService>();
        _mockMapService = new Mock<IMapService>();
        
        _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<Vector2>())).Returns(true);
        
        // Act
        _movementService.MovePlayer(playerId, direction);
        
        // Assert
        _mockPlayerRepository.Verify(r => r.Update(It.Is<Player>(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<Vector2>())).Returns(false);
        
        // Act
        _movementService.MovePlayer(playerId, direction);
        
        // Assert
        _mockPlayerRepository.Verify(r => r.Update(It.IsAny<Player>()), Times.Never);
    }
}

// ❌ 避免:难以测试的设计
public class PlayerMovementSystem : AbstractSystem
{
    protected override void OnInit()
    {
        this.RegisterEvent<MovementInputEvent>(OnMovementInput);
    }
    
    private void OnMovementInput(MovementInputEvent e)
    {
        var player = GetModel<PlayerModel>(); // 依赖架构,难以测试
        var newPosition = player.Position + e.Direction * player.Speed;
        
        if (CanMoveTo(newPosition)) // 私有方法,难以直接测试
        {
            player.Position = newPosition;
        }
    }
    
    private bool CanMoveTo(Vector2 position)
    {
        // 复杂的碰撞检测逻辑,难以测试
        return true;
    }
}

重构指南

1. 识别代码异味

// ❌ 代码异味:长方法、重复代码、上帝类
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<InputEvent>(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<PlayerMoveEvent>(OnPlayerMove);
    }
    
    private void OnPlayerMove(PlayerMoveEvent e)
    {
        var playerModel = GetModel<PlayerModel>();
        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<ICollisionService>();
        return collisionService.CanMoveTo(position);
    }
}

public class AudioSystem : AbstractSystem
{
    protected override void OnInit()
    {
        this.RegisterEvent<PlayerMovedEvent>(OnPlayerMoved);
    }
    
    private void OnPlayerMoved(PlayerMovedEvent e)
    {
        PlayFootstepSound();
    }
    
    private void PlayFootstepSound()
    {
        var audioUtility = GetUtility<AudioUtility>();
        audioUtility.PlaySound("footstep.wav");
    }
}

public class AnimationSystem : AbstractSystem
{
    protected override void OnInit()
    {
        this.RegisterEvent<PlayerMovedEvent>(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. 渐进式重构

// 第一步:提取重复代码
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<InputEvent>(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

// 推荐组合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 获取服务
    }
}

优势

  • 快速开发
  • 代码简洁
  • 易于理解

中型项目(独立游戏)

// 推荐组合MVC + MVVM + 命令/查询 + 事件驱动 + 依赖注入
public class GameArchitecture : Architecture
{
    protected override void Init()
    {
        // 注册 Utility依赖注入
        RegisterUtility&lt;IStorageService&gt;(new LocalStorageService());
        RegisterUtility&lt;IAudioService&gt;(new GodotAudioService());

        // 注册 ModelMVVM
        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&lt;IStateMachineSystem&gt;(stateMachine);
    }
}

优势

  • 职责清晰
  • 易于测试
  • 支持团队协作

大型项目(商业游戏)

// 推荐组合:所有模式 + 模块化架构
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&lt;IStorageService&gt;(new CloudStorageService());
        architecture.RegisterUtility&lt;IAnalyticsService&gt;(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&lt;IStateMachineSystem&gt;(stateMachine);
    }
}

优势

  • 高度模块化
  • 易于维护和扩展
  • 支持大型团队
  • 完善的测试覆盖

模式组合示例

组合 1MVVM + 命令模式

// ViewModel
public class ShopViewModel : AbstractModel
{
    public BindableProperty&lt;int&gt; PlayerGold { get; } = new(1000);
    public BindableProperty&lt;bool&gt; CanBuy { get; } = new(true);
    public BindableProperty&lt;string&gt; StatusMessage { get; } = new("");

    protected override void OnInit()
    {
        // 监听购买事件
        this.RegisterEvent&lt;ItemPurchasedEvent&gt;(OnItemPurchased);
        this.RegisterEvent&lt;InsufficientGoldEvent&gt;(OnInsufficientGold);

        // 监听金币变化
        PlayerGold.Register(gold =>
        {
            CanBuy.Value = gold &gt;= 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&lt;ShopViewModel&gt;();

        // 数据绑定
        _viewModel.PlayerGold.Register(gold =>
        {
            GetNode&lt;Label&gt;("GoldLabel").Text = $"金币:{gold}";
        });

        _viewModel.CanBuy.Register(canBuy =>
        {
            GetNode&lt;Button&gt;("BuyButton").Disabled = !canBuy;
        });

        _viewModel.StatusMessage.Register(msg =>
        {
            GetNode&lt;Label&gt;("StatusLabel").Text = msg;
        });
    }

    private void OnBuyButtonPressed()
    {
        // 发送命令
        this.SendCommand(new BuyItemCommand
        {
            Input = new BuyItemInput { ItemId = "sword_01" }
        });
    }
}

组合 2状态模式 + 对象池

// 游戏状态使用对象池
public class GameplayState : ContextAwareStateBase
{
    private BulletPoolSystem _bulletPool;
    private ParticlePoolSystem _particlePool;

    public override void OnEnter(IState from)
    {
        // 获取对象池
        _bulletPool = this.GetSystem&lt;BulletPoolSystem&gt;();
        _particlePool = this.GetSystem&lt;ParticlePoolSystem&gt;();

        // 预热对象池
        _bulletPool.PrewarmPool(100);
        _particlePool.PrewarmPool(200);

        // 注册事件
        this.RegisterEvent&lt;FireWeaponEvent&gt;(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事件驱动 + 查询模式

// 成就系统:监听事件,使用查询验证条件
public class AchievementSystem : AbstractSystem
{
    protected override void OnInit()
    {
        this.RegisterEvent&lt;EnemyKilledEvent&gt;(OnEnemyKilled);
        this.RegisterEvent&lt;LevelCompletedEvent&gt;(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:

  • 使用事件

    • 通知状态变化
    • 一对多通信
    • 跨模块通信
    • 不关心处理结果
  • 使用命令

    • 执行具体操作
    • 需要封装逻辑
    • 需要撤销/重做
    • 需要返回结果

示例

// 使用命令执行操作
this.SendCommand(new BuyItemCommand());

// 使用事件通知结果
this.SendEvent(new ItemPurchasedEvent());

Q4: 依赖注入和服务定位器哪个更好?

A:

  • 依赖注入

    • 依赖关系明确
    • 易于测试
    • 编译时检查
    • 配置复杂
  • 服务定位器

    • 简单直接
    • 易于使用
    • 依赖隐式
    • 难以测试

推荐

  • 小项目:服务定位器
  • 大项目:依赖注入
  • 混合使用:核心服务用依赖注入,辅助服务用服务定位器

Q5: 对象池适合哪些场景?

A:

适合

  • 频繁创建/销毁的对象(子弹、粒子)
  • 创建成本高的对象(网络连接)
  • 需要稳定帧率的场景

不适合

  • 创建频率低的对象
  • 对象状态复杂难以重置
  • 内存受限的场景

示例

// ✅ 适合使用对象池
- 子弹、导弹
- 粒子效果
- UI 元素(列表项)
- 音效播放器

// ❌ 不适合使用对象池
- 玩家角色
- 关卡数据
- 配置对象

Q6: 状态机和简单的 if-else 有什么区别?

A:

简单 if-else

// ❌ 难以维护
public void Update()
{
    if (gameState == GameState.Menu)
    {
        UpdateMenu();
    }
    else if (gameState == GameState.Playing)
    {
        UpdateGameplay();
    }
    else if (gameState == GameState.Paused)
    {
        UpdatePause();
    }
    // 状态逻辑分散,难以管理
}

状态机

// ✅ 清晰易维护
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

示例

// 第一版:简单直接
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&lt;int&gt; OnHealthChanged;
}

// 第三版:需要更多功能时使用 BindableProperty
public class PlayerModel : AbstractModel
{
    public BindableProperty&lt;int&gt; Health { get; } = new(100);
}

Q8: 如何在现有项目中引入这些模式?

A:

渐进式重构

  1. 第一步:引入事件系统

    // 替换直接调用为事件
    // 之前uiManager.UpdateHealth(health);
    // 之后SendEvent(new HealthChangedEvent { Health = health });
    
  2. 第二步:提取 Model

    // 将数据从各处集中到 Model
    public class PlayerModel : AbstractModel
    {
        public BindableProperty&lt;int&gt; Health { get; } = new(100);
    }
    
  3. 第三步:引入命令模式

    // 封装操作为命令
    public class HealPlayerCommand : AbstractCommand
    {
        protected override void OnExecute()
        {
            var player = this.GetModel&lt;PlayerModel&gt;();
            player.Health.Value = player.MaxHealth.Value;
        }
    }
    
  4. 第四步:添加查询模式

    // 分离读操作
    public class GetPlayerStatsQuery : AbstractQuery&lt;PlayerStats&gt;
    {
        protected override PlayerStats OnDo()
        {
            // 查询逻辑
        }
    }
    

Q9: 性能会受到影响吗?

A:

影响很小

  • 事件系统:微秒级开销
  • 命令/查询:几乎无开销
  • IoC 容器字典查找O(1)

优化建议

  1. 避免频繁事件:不要每帧发送事件
  2. 缓存查询结果:复杂查询结果可以缓存
  3. 使用对象池:减少 GC 压力
  4. 批量操作:合并多个小操作

性能对比

// 直接调用:~1ns
player.Health = 100;

// 通过命令:~100ns
SendCommand(new SetHealthCommand { Health = 100 });

// 差异可以忽略不计,但带来了更好的架构

Q10: 如何测试使用这些模式的代码?

A:

单元测试示例

[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. 专家:模块化架构 + 所有模式组合

相关资源

记住,好的架构不是一蹴而就的,需要持续的学习、实践和改进。


文档版本: 2.0.0 最后更新: 2026-03-07 作者: GFramework Team