mirror of
https://github.com/GeWuYou/GFramework.git
synced 2026-03-22 19:03:29 +08:00
- 新增 Architecture 基类与 IArchitecture 接口,实现单例模式与组件注册管理 - 集成 IOC 容器支持系统、模型、工具的依赖注入与生命周期管理 - 实现命令模式基础类 AbstractCommand 与接口 ICommand,支持带返回值命令 - 提供事件系统集成,支持事件的发布与订阅机制 - 添加控制器接口 IController,整合命令发送、事件注册与模型获取能力 - 创建详细的 README 文档说明各组件使用方式与设计模式应用 - 支持命令、查询、事件的统一调度与解耦通信机制
584 lines
14 KiB
Markdown
584 lines
14 KiB
Markdown
# 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 扩展方法 |