GwWuYou 5aa11ddc41 feat(architecture): 添加架构核心组件与命令模式实现
- 新增 Architecture 基类与 IArchitecture 接口,实现单例模式与组件注册管理
- 集成 IOC 容器支持系统、模型、工具的依赖注入与生命周期管理
- 实现命令模式基础类 AbstractCommand 与接口 ICommand,支持带返回值命令
- 提供事件系统集成,支持事件的发布与订阅机制
- 添加控制器接口 IController,整合命令发送、事件注册与模型获取能力
- 创建详细的 README 文档说明各组件使用方式与设计模式应用
- 支持命令、查询、事件的统一调度与解耦通信机制
2025-12-09 15:32:17 +08:00

584 lines
14 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Utility 包使用说明
## 概述
Utility 包定义了工具类层。Utility 提供无状态的辅助功能,如数学计算、文件操作、序列化等通用工具方法。与 System 不同Utility 不依赖架构状态,是纯粹的工具函数集合。
## 核心接口
### [`ICanGetUtility`](ICanGetUtility.cs)
标记接口,表示该类型可以获取 Utility。
**继承关系:**
```csharp
public interface ICanGetUtility : IBelongToArchitecture
```
### [`IUtility`](IUtility.cs)
Utility 标记接口,所有工具类都应实现此接口。
**接口定义:**
```csharp
public interface IUtility
{
// 标记接口,无方法定义
}
```
## 基本使用
### 1. 定义 Utility
```csharp
// 存储工具类
public class StorageUtility : IUtility
{
private const string SavePath = "user://save_data.json";
public void Save<T>(T data)
{
string json = Json.Stringify(data);
using var file = FileAccess.Open(SavePath, FileAccess.ModeFlags.Write);
file.StoreString(json);
}
public T Load<T>() where T : new()
{
if (!FileAccess.FileExists(SavePath))
return new T();
using var file = FileAccess.Open(SavePath, FileAccess.ModeFlags.Read);
string json = file.GetAsText();
return Json.Parse<T>(json);
}
public void Delete()
{
if (FileAccess.FileExists(SavePath))
{
DirAccess.RemoveAbsolute(SavePath);
}
}
}
// 数学工具类
public class MathUtility : IUtility
{
public float Lerp(float a, float b, float t)
{
return a + (b - a) * Mathf.Clamp(t, 0f, 1f);
}
public Vector3 BezierCurve(Vector3 p0, Vector3 p1, Vector3 p2, float t)
{
float u = 1 - t;
return u * u * p0 + 2 * u * t * p1 + t * t * p2;
}
public bool IsInRange(Vector3 point, Vector3 center, float radius)
{
return point.DistanceTo(center) <= radius;
}
public int RollDice(int sides)
{
return GD.RandRange(1, sides);
}
}
// 时间工具类
public class TimeUtility : IUtility
{
public string FormatTime(float seconds)
{
int minutes = (int)(seconds / 60);
int secs = (int)(seconds % 60);
return $"{minutes:D2}:{secs:D2}";
}
public long GetCurrentTimestamp()
{
return DateTimeOffset.UtcNow.ToUnixTimeSeconds();
}
public bool IsExpired(long timestamp, int durationSeconds)
{
return GetCurrentTimestamp() > timestamp + durationSeconds;
}
}
```
### 2. 注册 Utility
```csharp
public class GameArchitecture : Architecture<GameArchitecture>
{
protected override void Init()
{
// 注册 Utility不需要初始化
this.RegisterUtility<StorageUtility>(new StorageUtility());
this.RegisterUtility<MathUtility>(new MathUtility());
this.RegisterUtility<TimeUtility>(new TimeUtility());
}
}
```
### 3. 使用 Utility
```csharp
// 在 System 中使用
public class SaveSystem : AbstractSystem
{
protected override void OnInit()
{
this.RegisterEvent<SaveGameEvent>(OnSaveGame);
this.RegisterEvent<LoadGameEvent>(OnLoadGame);
}
private void OnSaveGame(SaveGameEvent e)
{
var storage = this.GetUtility<StorageUtility>();
var playerModel = this.GetModel<PlayerModel>();
var saveData = new SaveData
{
PlayerName = playerModel.Name.Value,
Level = playerModel.Level.Value,
Gold = playerModel.Gold.Value,
Timestamp = this.GetUtility<TimeUtility>().GetCurrentTimestamp()
};
storage.Save(saveData);
this.SendEvent(new GameSavedEvent());
}
private void OnLoadGame(LoadGameEvent e)
{
var storage = this.GetUtility<StorageUtility>();
var saveData = storage.Load<SaveData>();
if (saveData != null)
{
var playerModel = this.GetModel<PlayerModel>();
playerModel.Name.Value = saveData.PlayerName;
playerModel.Level.Value = saveData.Level;
playerModel.Gold.Value = saveData.Gold;
this.SendEvent(new GameLoadedEvent());
}
}
}
// 在 Command 中使用
public class MovePlayerCommand : AbstractCommand
{
public Vector3 TargetPosition { get; set; }
public float Speed { get; set; }
protected override void OnExecute()
{
var playerModel = this.GetModel<PlayerModel>();
var mathUtil = this.GetUtility<MathUtility>();
// 使用工具类计算
Vector3 currentPos = playerModel.Position.Value;
Vector3 direction = (TargetPosition - currentPos).Normalized();
Vector3 newPos = currentPos + direction * Speed;
playerModel.Position.Value = newPos;
}
}
```
## 常见 Utility 类型
### 1. 序列化/反序列化工具
```csharp
public class JsonUtility : IUtility
{
public string Serialize<T>(T obj)
{
return Json.Stringify(obj);
}
public T Deserialize<T>(string json) where T : new()
{
return Json.Parse<T>(json);
}
public bool TryDeserialize<T>(string json, out T result) where T : new()
{
try
{
result = Json.Parse<T>(json);
return true;
}
catch
{
result = default;
return false;
}
}
}
```
### 2. 随机数工具
```csharp
public class RandomUtility : IUtility
{
private Random _random = new Random();
public int Range(int min, int max)
{
return _random.Next(min, max + 1);
}
public float Range(float min, float max)
{
return min + (float)_random.NextDouble() * (max - min);
}
public T Choose<T>(params T[] items)
{
return items[Range(0, items.Length - 1)];
}
public List<T> Shuffle<T>(List<T> list)
{
var shuffled = new List<T>(list);
for (int i = shuffled.Count - 1; i > 0; i--)
{
int j = Range(0, i);
(shuffled[i], shuffled[j]) = (shuffled[j], shuffled[i]);
}
return shuffled;
}
public bool Probability(float chance)
{
return _random.NextDouble() < chance;
}
}
```
### 3. 字符串工具
```csharp
public class StringUtility : IUtility
{
public string Truncate(string text, int maxLength, string suffix = "...")
{
if (text.Length <= maxLength)
return text;
return text.Substring(0, maxLength - suffix.Length) + suffix;
}
public string FormatNumber(int number)
{
if (number >= 1000000)
return $"{number / 1000000.0:F1}M";
if (number >= 1000)
return $"{number / 1000.0:F1}K";
return number.ToString();
}
public string ToTitleCase(string text)
{
return System.Globalization.CultureInfo.CurrentCulture.TextInfo.ToTitleCase(text.ToLower());
}
public bool IsValidEmail(string email)
{
return Regex.IsMatch(email, @"^[^@\s]+@[^@\s]+\.[^@\s]+$");
}
}
```
### 4. 加密工具
```csharp
public class EncryptionUtility : IUtility
{
private const string EncryptionKey = "YourSecretKey123";
public string Encrypt(string plainText)
{
byte[] data = System.Text.Encoding.UTF8.GetBytes(plainText);
byte[] encrypted = EncryptBytes(data);
return Convert.ToBase64String(encrypted);
}
public string Decrypt(string encryptedText)
{
byte[] data = Convert.FromBase64String(encryptedText);
byte[] decrypted = DecryptBytes(data);
return System.Text.Encoding.UTF8.GetString(decrypted);
}
private byte[] EncryptBytes(byte[] data)
{
// 简单的 XOR 加密示例(实际项目应使用更安全的算法)
byte[] key = System.Text.Encoding.UTF8.GetBytes(EncryptionKey);
byte[] result = new byte[data.Length];
for (int i = 0; i < data.Length; i++)
{
result[i] = (byte)(data[i] ^ key[i % key.Length]);
}
return result;
}
private byte[] DecryptBytes(byte[] data)
{
return EncryptBytes(data); // XOR 解密与加密相同
}
}
```
### 5. 对象池工具
```csharp
public class ObjectPoolUtility : IUtility
{
private Dictionary<Type, Queue<object>> _pools = new();
public T Get<T>() where T : new()
{
Type type = typeof(T);
if (_pools.ContainsKey(type) && _pools[type].Count > 0)
{
return (T)_pools[type].Dequeue();
}
return new T();
}
public void Return<T>(T obj)
{
Type type = typeof(T);
if (!_pools.ContainsKey(type))
{
_pools[type] = new Queue<object>();
}
_pools[type].Enqueue(obj);
}
public void Clear<T>()
{
Type type = typeof(T);
if (_pools.ContainsKey(type))
{
_pools[type].Clear();
}
}
public void ClearAll()
{
_pools.Clear();
}
}
```
### 6. 日志工具
```csharp
public class LogUtility : IUtility
{
public enum LogLevel
{
Debug,
Info,
Warning,
Error
}
public void Log(string message, LogLevel level = LogLevel.Info)
{
string prefix = level switch
{
LogLevel.Debug => "[DEBUG]",
LogLevel.Info => "[INFO]",
LogLevel.Warning => "[WARN]",
LogLevel.Error => "[ERROR]",
_ => ""
};
string timestamp = DateTime.Now.ToString("HH:mm:ss");
GD.Print($"{timestamp} {prefix} {message}");
}
public void Debug(string message) => Log(message, LogLevel.Debug);
public void Info(string message) => Log(message, LogLevel.Info);
public void Warning(string message) => Log(message, LogLevel.Warning);
public void Error(string message) => Log(message, LogLevel.Error);
}
```
## Utility vs System
### Utility工具层
- **无状态** - 不存储业务数据
- **纯函数** - 相同输入产生相同输出
- **独立性** - 不依赖架构状态
- **可复用** - 可在多个项目中使用
### System逻辑层
- **有状态** - 可能存储临时状态
- **业务逻辑** - 处理特定业务流程
- **架构依赖** - 需要访问 Model
- **项目特定** - 针对特定项目设计
```csharp
// ✅ Utility: 无状态的工具方法
public class MathUtility : IUtility
{
public float CalculateDamage(float attackPower, float defense)
{
return Math.Max(1, attackPower - defense * 0.5f);
}
}
// ✅ System: 有状态的业务逻辑
public class CombatSystem : AbstractSystem
{
private List<CombatInstance> _activeCombats = new();
protected override void OnInit()
{
this.RegisterEvent<AttackEvent>(OnAttack);
}
private void OnAttack(AttackEvent e)
{
var mathUtil = this.GetUtility<MathUtility>();
var playerModel = this.GetModel<PlayerModel>();
// 使用 Utility 计算,但在 System 中处理业务逻辑
float damage = mathUtil.CalculateDamage(e.AttackPower, playerModel.Defense.Value);
playerModel.Health.Value -= (int)damage;
_activeCombats.Add(new CombatInstance { Damage = damage });
}
}
```
## 最佳实践
1. **保持无状态** - Utility 不应存储业务状态
2. **纯函数优先** - 相同输入应产生相同输出
3. **单一职责** - 每个 Utility 专注于一类功能
4. **避免依赖** - 不依赖 Model、System 等架构组件
5. **可测试** - 易于单元测试的纯函数
## 错误示例
```csharp
// ❌ 错误Utility 中存储状态
public class BadUtility : IUtility
{
private int _counter = 0; // 不应该有状态
public int GetNextId()
{
return ++_counter; // 依赖内部状态
}
}
// ❌ 错误Utility 中访问 Model
public class BadUtility2 : IUtility
{
public void DoSomething()
{
// Utility 不应该访问架构组件
var model = this.GetModel<PlayerModel>(); // 编译错误!
}
}
// ✅ 正确:如果需要状态,应该使用 System
public class IdGeneratorSystem : AbstractSystem
{
private int _counter = 0;
protected override void OnInit() { }
public int GetNextId()
{
return ++_counter;
}
}
```
## 性能优化
### 1. 缓存计算结果
```csharp
public class PathfindingUtility : IUtility
{
private Dictionary<(Vector3, Vector3), List<Vector3>> _pathCache = new();
public List<Vector3> FindPath(Vector3 start, Vector3 end, bool useCache = true)
{
var key = (start, end);
if (useCache && _pathCache.ContainsKey(key))
{
return _pathCache[key];
}
var path = CalculatePath(start, end);
if (useCache)
{
_pathCache[key] = path;
}
return path;
}
private List<Vector3> CalculatePath(Vector3 start, Vector3 end)
{
// A* 算法等复杂计算...
return new List<Vector3>();
}
public void ClearCache()
{
_pathCache.Clear();
}
}
```
### 2. 对象复用
```csharp
public class CollectionUtility : IUtility
{
private List<Vector3> _tempList = new();
public List<Vector3> GetPointsInRadius(Vector3 center, float radius, List<Vector3> points)
{
_tempList.Clear();
foreach (var point in points)
{
if (point.DistanceTo(center) <= radius)
{
_tempList.Add(point);
}
}
return new List<Vector3>(_tempList); // 返回副本
}
}
```
## 相关包
- [`system`](../system/README.md) - System 中使用 Utility
- [`command`](../command/README.md) - Command 中可以使用 Utility
- [`architecture`](../architecture/README.md) - 在架构中注册 Utility
- [`ioc`](../ioc/README.md) - Utility 通过 IoC 容器管理
- [`extensions`](../extensions/README.md) - 提供 GetUtility 扩展方法