diff --git a/.vitepress/config.mts b/.vitepress/config.mts index 9a702bb..a3aa2d0 100644 --- a/.vitepress/config.mts +++ b/.vitepress/config.mts @@ -20,6 +20,7 @@ const enSidebar = [ { text: 'Factory Registry', link: '/factory-registry' }, { text: 'Decorator', link: '/decorator' }, { text: 'Event Aggregator', link: '/event-aggregator' }, + { text: 'State transition table', link: '/state-transition-table' }, { text: 'Dependency Injection', link: '/dependency-injection' }, ], }, @@ -44,6 +45,7 @@ const zhSidebar = [ { text: 'Factory Registry', link: '/zh/factory-registry' }, { text: 'Decorator', link: '/zh/decorator' }, { text: 'Event Aggregator', link: '/zh/event-aggregator' }, + { text: 'State 转换表', link: '/zh/state-transition-table' }, { text: '依赖注入', link: '/zh/dependency-injection' }, ], }, diff --git a/docs/diagnostics.md b/docs/diagnostics.md index d08c6c3..0752d8f 100644 --- a/docs/diagnostics.md +++ b/docs/diagnostics.md @@ -73,6 +73,19 @@ Help links in the IDE point to this page (`#dp###` anchors). See also [Registry key conventions](./registry-key-conventions.md). +## State transition table (DP026–DP031) {#state-transition-table-dp026-dp031} + +| ID | Severity | When | +|----|----------|------| +| **DP026** | Error | Duplicate `(from state, trigger)` edge | +| **DP027** | Error | `[Transition]` state value is not a declared enum member | +| **DP028** | Error | `[Transition]` trigger value is not a declared enum member | +| **DP029** | Error | `[StateMachine]` `Initial` is not a declared state enum member | +| **DP030** | Error | Holder class is not `static partial` | +| **DP031** | Info | State enum member never appears as a `[Transition]` source (terminal/reserved hint) | + +See [State transition table](./state-transition-table.md). + ## Code fixes `DesignPatterns.CodeFixes` ships inside the **`Skymly.DesignPatterns`** meta package (`analyzers/dotnet/cs`). Selected diagnostics offer one-click fixes in the IDE (requires C# Workspaces): diff --git a/docs/samples.md b/docs/samples.md index 7c079ce..55b8fa8 100644 --- a/docs/samples.md +++ b/docs/samples.md @@ -36,6 +36,7 @@ The main [DesignPatterns](https://github.com/Skymly/DesignPatterns) CI checks ou | **DesignPatterns.Samples.EventAggregator** | `IEventAggregator` pub/sub | | **DesignPatterns.Samples.GenerateSingleton** | `[GenerateSingleton]` | | **DesignPatterns.Samples.DependencyInjection** | `RegisterDi` for Strategy / Factory / Handler | +| **DesignPatterns.Samples.State** | Manual `TransitionTableBuilder` + `[StateMachine]` order lifecycle | ## NuGet consumption diff --git a/docs/state-transition-table.md b/docs/state-transition-table.md new file mode 100644 index 0000000..447d7ce --- /dev/null +++ b/docs/state-transition-table.md @@ -0,0 +1,53 @@ +# State transition table + +Namespace: `DesignPatterns.Behavioral` + +## Overview + +Model finite state graphs as **(current state, trigger) → next state**. Use a manual builder or compile-time `[StateMachine]` / `[Transition]` attributes to avoid hand-written `switch` blocks and catch invalid edges at build time. + +This is **not** a full UML state-machine framework (no hierarchical states, history, persistence, or entry/exit action DSL). + +## Runtime + +- `ITransitionTable` — `TryTransition`, `GetAllowedTriggers`, `CanTransitionFrom` +- `TransitionTableBuilder` — `WithInitial`, `Add`, `Build` +- `Transition()` extension — throws `InvalidTransitionException` on invalid edges + +`TState` and `TTrigger` must be **enums** (v1). + +```csharp +var table = new TransitionTableBuilder() + .WithInitial(OrderStatus.Draft) + .Add(OrderStatus.Draft, OrderTrigger.Submit, OrderStatus.Submitted) + .Add(OrderStatus.Submitted, OrderTrigger.Pay, OrderStatus.Paid) + .Build(); + +table.TryTransition(OrderStatus.Draft, OrderTrigger.Submit, out var next); +``` + +## Source generator + +1. Define separate **state** and **trigger** enums. +2. Declare a **static partial** holder class with `[StateMachine(typeof(TState), typeof(TTrigger), Initial = ...)]`. +3. Add one or more `[Transition(from, trigger, to)]` attributes on the holder. + +```csharp +[StateMachine(typeof(OrderStatus), typeof(OrderTrigger), Initial = OrderStatus.Draft)] +[Transition(OrderStatus.Draft, OrderTrigger.Submit, OrderStatus.Submitted)] +[Transition(OrderStatus.Submitted, OrderTrigger.Pay, OrderStatus.Paid)] +public static partial class OrderMachine; + +// Generated: OrderStatusTransitionTable.Instance +// Holder: OrderMachine.TryTransition(...), OrderMachine.InitialState +``` + +## Diagnostics + +DP026–DP031 — duplicate edges, invalid enum members, invalid holder, isolated states. See [Diagnostics](./diagnostics.md#state-transition-table-dp026-dp031). + +## Sample + +[DesignPatterns.Samples.State](https://github.com/Skymly/DesignPatterns.Samples/tree/main/DesignPatterns.Samples.State) + +Maintainer doc: [docs/StateTransitionTable.md](https://github.com/Skymly/DesignPatterns/blob/main/docs/StateTransitionTable.md) (中文). diff --git a/docs/strategy.md b/docs/strategy.md index b9a74e7..230a121 100644 --- a/docs/strategy.md +++ b/docs/strategy.md @@ -49,7 +49,7 @@ DP003–DP007 — duplicate keys, contract mismatch, unregistered types (DP006 + ## Sample -[DesignPatterns.Samples.Strategy](https://github.com/Skymly/DesignPatterns.Samples/tree/main/DesignPatterns.Samples.Strategy) +[DesignPatterns.Samples.Strategy](https://github.com/Skymly/DesignPatterns.Samples/tree/main/DesignPatterns.Samples.Strategy) — sync payment strategies plus async `IRefundProcessor` with `ExecuteAsync` (`RefundProcessors.cs`). ## DI diff --git a/docs/zh/diagnostics.md b/docs/zh/diagnostics.md index 7729fea..e9bc291 100644 --- a/docs/zh/diagnostics.md +++ b/docs/zh/diagnostics.md @@ -72,6 +72,19 @@ IDE 帮助链接指向本页(`#dp###` 锚点)。 另见 [注册表 Key 命名约定](./registry-key-conventions.md)。 +## State 转换表(DP026–DP031) {#state-转换表-dp026-dp031} + +| ID | 级别 | 触发条件 | +|----|------|----------| +| **DP026** | Error | `(from state, trigger)` 边重复 | +| **DP027** | Error | `[Transition]` 的 state 非 enum 成员 | +| **DP028** | Error | trigger 非 enum 成员 | +| **DP029** | Error | `[StateMachine]` 的 `Initial` 非 state enum 成员 | +| **DP030** | Error | holder 不是 `static partial` class | +| **DP031** | Info | state enum 成员从未作为 `[Transition]` 的 from(终态提示) | + +见 [State 转换表](./state-transition-table.md)。 + ## CodeFix `DesignPatterns.CodeFixes` 随 **`Skymly.DesignPatterns`** 元包分发(`analyzers/dotnet/cs`)。下列诊断支持 IDE 一键修复(需 C# Workspaces): diff --git a/docs/zh/samples.md b/docs/zh/samples.md index 5f53611..3454ae9 100644 --- a/docs/zh/samples.md +++ b/docs/zh/samples.md @@ -36,6 +36,7 @@ dotnet run --project DesignPatterns.Samples.Strategy -c Release | **DesignPatterns.Samples.EventAggregator** | 事件聚合器 | | **DesignPatterns.Samples.GenerateSingleton** | `[GenerateSingleton]` | | **DesignPatterns.Samples.DependencyInjection** | Strategy / Factory / Handler 的 `RegisterDi` | +| **DesignPatterns.Samples.State** | 手动 `TransitionTableBuilder` + `[StateMachine]` 订单生命周期 | ## NuGet 消费 diff --git a/docs/zh/state-transition-table.md b/docs/zh/state-transition-table.md new file mode 100644 index 0000000..4aefb67 --- /dev/null +++ b/docs/zh/state-transition-table.md @@ -0,0 +1,55 @@ +# State 转换表 + +命名空间:`DesignPatterns.Behavioral` + +## 概览 + +用 **(当前状态, 触发器) → 下一状态** 描述有限状态图。可用手动 Builder,或用编译期 `[StateMachine]` / `[Transition]` 减少手写 `switch` 并在构建期发现非法边。 + +**不是**完整 UML 状态机框架(无层次状态、历史、持久化或 entry/exit 动作 DSL)。 + +## 运行时 + +- `ITransitionTable` — `TryTransition`、`GetAllowedTriggers`、`CanTransitionFrom` +- `TransitionTableBuilder` — `WithInitial`、`Add`、`Build` +- `Transition()` 扩展 — 非法边时抛 `InvalidTransitionException` + +v1 要求 `TState`、`TTrigger` 均为 **enum**。 + +```csharp +var table = new TransitionTableBuilder() + .WithInitial(OrderStatus.Draft) + .Add(OrderStatus.Draft, OrderTrigger.Submit, OrderStatus.Submitted) + .Add(OrderStatus.Submitted, OrderTrigger.Pay, OrderStatus.Paid) + .Build(); + +table.TryTransition(OrderStatus.Draft, OrderTrigger.Submit, out var next); +``` + +## 源生成器 + +1. 分别定义 **state enum** 与 **trigger enum**。 +2. 声明带 `[StateMachine(typeof(TState), typeof(TTrigger), Initial = ...)]` 的 **static partial** holder。 +3. 在 holder 上标注一条或多条 `[Transition(from, trigger, to)]`。 + +```csharp +[StateMachine(typeof(OrderStatus), typeof(OrderTrigger), Initial = OrderStatus.Draft)] +[Transition(OrderStatus.Draft, OrderTrigger.Submit, OrderStatus.Submitted)] +[Transition(OrderStatus.Submitted, OrderTrigger.Pay, OrderStatus.Paid)] +public static partial class OrderMachine; + +// 生成:OrderStatusTransitionTable.Instance +// holder:OrderMachine.TryTransition(...)、OrderMachine.InitialState +``` + +## 诊断 + +DP026–DP031 — 重复边、非法 enum 成员、非法 holder、孤立态等。见 [诊断](./diagnostics.md#state-转换表-dp026-dp031)。 + +## 示例 + +[DesignPatterns.Samples.State](https://github.com/Skymly/DesignPatterns.Samples/tree/main/DesignPatterns.Samples.State) + +维护者文档:[docs/StateTransitionTable.md](https://github.com/Skymly/DesignPatterns/blob/main/docs/StateTransitionTable.md) + +英文完整版:[State transition table](../state-transition-table.md) diff --git a/docs/zh/strategy.md b/docs/zh/strategy.md index 14cf6bd..dd30260 100644 --- a/docs/zh/strategy.md +++ b/docs/zh/strategy.md @@ -42,7 +42,7 @@ DP003–DP007;字面量 key 见 [DP025](./diagnostics.md#注册表-key-dp025) ## 示例 -[DesignPatterns.Samples.Strategy](https://github.com/Skymly/DesignPatterns.Samples/tree/main/DesignPatterns.Samples.Strategy) +[DesignPatterns.Samples.Strategy](https://github.com/Skymly/DesignPatterns.Samples/tree/main/DesignPatterns.Samples.Strategy) — 同步支付策略与异步 `IRefundProcessor`(`ExecuteAsync`,见 `RefundProcessors.cs`)。 ## DI