diff --git a/docs/composite.md b/docs/composite.md index f34c35d..78e739d 100644 --- a/docs/composite.md +++ b/docs/composite.md @@ -4,32 +4,49 @@ Namespace: `DesignPatterns.Structural` ## Overview -Tree structures where leaves and composites share a node contract. The library helps catalog parts and build roots without deep inheritance. +Tree structures where leaves and composites share a node contract. The library catalogs parts at compile time and assembles single-root trees or multi-root forests at runtime. ## Runtime - `ICompositeNode` — node contract with children -- `CompositeTraverser` — depth-first traversal +- `ICompositeBuildable` — receives assembled children via `SetChildren` +- `CompositeCatalogAssembler.Assemble` / `AssembleForest` — build from flat catalog entries +- `CompositeTraverser.Traverse` / `TraverseForest` — depth-first (pre/post-order) and breadth-first traversal - `CompositeTreeBuilder` — manual tree construction ## Source generator -`[CompositePart]` on implementations; `[CompositeBuildable]` on a partial catalog type. Generator emits keys, catalog, and `BuildRoot()`. +Mark each implementation with `[CompositePart]` (generic on .NET 7+). The generator emits `{Contract}CompositeKeys`, `{Contract}CompositeCatalog`, `BuildRoot()`, and `BuildForest()`. ```csharp -[CompositePart("home", IsRoot = true)] -public sealed partial class HomeMenu : IMenuNode { ... } +[CompositePart("root")] +public sealed class HomeMenu : IMenuNode, ICompositeBuildable { ... } -[CompositePart("settings", ParentKey = "home")] -public sealed partial class SettingsMenu : IMenuNode { ... } +[CompositePart("admin", Order = 5)] +public sealed class AdminMenu : IMenuNode, ICompositeBuildable { ... } + +[CompositePart("settings", ParentKey = "root", Order = 10)] +public sealed class SettingsMenu : IMenuNode, ICompositeBuildable { ... } + +var root = MenuNodeCompositeCatalog.BuildRoot(); // exactly one ParentKey == null +var forest = MenuNodeCompositeCatalog.BuildForest(); // one or more roots + +CompositeTraverser.TraverseForest(forest, (node, depth, rootIndex) => { ... }); ``` +| API | When to use | +|-----|-------------| +| `BuildRoot()` | Catalog has **exactly one** `ParentKey == null` entry | +| `BuildForest()` | Catalog has **one or more** roots (ordered by `Order`, then key) | + +Multi-root catalogs: `BuildRoot()` throws `CompositeAssemblyException` at runtime. + ## Diagnostics DP010–DP015. ## Sample -[DesignPatterns.Samples.Composite](https://github.com/Skymly/DesignPatterns.Samples/tree/main/DesignPatterns.Samples.Composite) — compares generated catalog vs manual builder. +[DesignPatterns.Samples.Composite](https://github.com/Skymly/DesignPatterns.Samples/tree/main/DesignPatterns.Samples.Composite) — generated `BuildForest()` + `TraverseForest`, `BuildRoot()` failure on multi-root catalog, and manual `CompositeTreeBuilder`. Maintainer doc: [docs/Composite.md](https://github.com/Skymly/DesignPatterns/blob/main/docs/Composite.md) (中文). diff --git a/docs/samples.md b/docs/samples.md index 2f441b6..024f387 100644 --- a/docs/samples.md +++ b/docs/samples.md @@ -29,7 +29,7 @@ CI checks out both repositories so the sibling path `../DesignPatterns` resolves |--------|--------------| | **DesignPatterns.Samples.Strategy** | `[RegisterStrategy]` → Keys + static `Instance` | | **DesignPatterns.Samples.Chain** | `[HandlerOrder]` → handler pipeline | -| **DesignPatterns.Samples.Composite** | `[CompositePart]` → catalog + `BuildRoot()` | +| **DesignPatterns.Samples.Composite** | `[CompositePart]` → `BuildForest()` / `TraverseForest` (+ manual builder) | | **DesignPatterns.Samples.Factory** | `[RegisterFactory]` generated registry | | **DesignPatterns.Samples.RegisterFactory** | Manual `FactoryRegistryBuilder` | | **DesignPatterns.Samples.Decorator** | `[Decorator]` decorator stack | diff --git a/docs/zh/composite.md b/docs/zh/composite.md index 824a83c..23ec25d 100644 --- a/docs/zh/composite.md +++ b/docs/zh/composite.md @@ -4,17 +4,37 @@ ## 概述 -叶子与组合节点共享契约的树结构。库提供部件目录与 `BuildRoot()`,减少深层继承。 +叶子与组合节点共享契约的树结构。库在编译期登记部件,运行时可装配单根树或多根森林。 ## 运行时 -- `ICompositeNode` -- `CompositeTraverser` — 深度优先遍历 +- `ICompositeNode` — 节点契约(含 `Children`) +- `ICompositeBuildable` — 通过 `SetChildren` 接收子节点 +- `CompositeCatalogAssembler.Assemble` / `AssembleForest` — 从 flat catalog 装配 +- `CompositeTraverser.Traverse` / `TraverseForest` — 深度优先(前/后序)与广度优先遍历 - `CompositeTreeBuilder` — 手动建树 ## 源生成器 -`[CompositePart]` 标记实现;`[CompositeBuildable]` 标记 partial 目录类型。 +在实现类上使用 `[CompositePart]`(.NET 7+ 可用泛型特性)。生成器产出 `{Contract}CompositeKeys`、`{Contract}CompositeCatalog`、`BuildRoot()` 与 `BuildForest()`。 + +```csharp +[CompositePart("root")] +public sealed class HomeMenu : IMenuNode, ICompositeBuildable { ... } + +[CompositePart("admin", Order = 5)] +public sealed class AdminMenu : IMenuNode, ICompositeBuildable { ... } + +var forest = MenuNodeCompositeCatalog.BuildForest(); +CompositeTraverser.TraverseForest(forest, (node, depth, rootIndex) => { ... }); +``` + +| API | 适用场景 | +|-----|----------| +| `BuildRoot()` | catalog 中**恰好一个** `ParentKey == null` | +| `BuildForest()` | **一个或多个**根(按 `Order` 再 key 排序) | + +多根 catalog 调用 `BuildRoot()` 会在运行时抛 `CompositeAssemblyException`。 ## 诊断 @@ -22,6 +42,6 @@ DP010–DP015。 ## 示例 -[DesignPatterns.Samples.Composite](https://github.com/Skymly/DesignPatterns.Samples/tree/main/DesignPatterns.Samples.Composite) +[DesignPatterns.Samples.Composite](https://github.com/Skymly/DesignPatterns.Samples/tree/main/DesignPatterns.Samples.Composite) — `BuildForest()` / `TraverseForest`、多根时 `BuildRoot()` 失败演示,以及手动 `CompositeTreeBuilder`。 维护者文档:[docs/Composite.md](https://github.com/Skymly/DesignPatterns/blob/main/docs/Composite.md) diff --git a/docs/zh/samples.md b/docs/zh/samples.md index e3adb68..fbf71f8 100644 --- a/docs/zh/samples.md +++ b/docs/zh/samples.md @@ -27,7 +27,7 @@ dotnet run --project DesignPatterns.Samples.Strategy -c Release |------|----------| | **DesignPatterns.Samples.Strategy** | `[RegisterStrategy]` → Keys + `Instance` | | **DesignPatterns.Samples.Chain** | `[HandlerOrder]` 管道 | -| **DesignPatterns.Samples.Composite** | `[CompositePart]` + `BuildRoot()` | +| **DesignPatterns.Samples.Composite** | `[CompositePart]` + `BuildForest()` / `TraverseForest`(含手动 builder) | | **DesignPatterns.Samples.Factory** | 生成器工厂注册表 | | **DesignPatterns.Samples.RegisterFactory** | 手动 `FactoryRegistryBuilder` | | **DesignPatterns.Samples.Decorator** | 装饰器栈 |