Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 25 additions & 8 deletions docs/composite.md
Original file line number Diff line number Diff line change
Expand Up @@ -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<TSelf>` — node contract with children
- `CompositeTraverser` — depth-first traversal
- `ICompositeBuildable<TNode>` — 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<TNode>` — 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<IMenuNode>("root")]
public sealed class HomeMenu : IMenuNode, ICompositeBuildable<IMenuNode> { ... }

[CompositePart("settings", ParentKey = "home")]
public sealed partial class SettingsMenu : IMenuNode { ... }
[CompositePart<IMenuNode>("admin", Order = 5)]
public sealed class AdminMenu : IMenuNode, ICompositeBuildable<IMenuNode> { ... }

[CompositePart<IMenuNode>("settings", ParentKey = "root", Order = 10)]
public sealed class SettingsMenu : IMenuNode, ICompositeBuildable<IMenuNode> { ... }

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) (中文).
2 changes: 1 addition & 1 deletion docs/samples.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 |
Expand Down
30 changes: 25 additions & 5 deletions docs/zh/composite.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,24 +4,44 @@

## 概述

叶子与组合节点共享契约的树结构。库提供部件目录与 `BuildRoot()`,减少深层继承
叶子与组合节点共享契约的树结构。库在编译期登记部件,运行时可装配单根树或多根森林

## 运行时

- `ICompositeNode<TSelf>`
- `CompositeTraverser` — 深度优先遍历
- `ICompositeNode<TSelf>` — 节点契约(含 `Children`)
- `ICompositeBuildable<TNode>` — 通过 `SetChildren` 接收子节点
- `CompositeCatalogAssembler.Assemble` / `AssembleForest` — 从 flat catalog 装配
- `CompositeTraverser.Traverse` / `TraverseForest` — 深度优先(前/后序)与广度优先遍历
- `CompositeTreeBuilder<TNode>` — 手动建树

## 源生成器

`[CompositePart]` 标记实现;`[CompositeBuildable]` 标记 partial 目录类型。
在实现类上使用 `[CompositePart]`(.NET 7+ 可用泛型特性)。生成器产出 `{Contract}CompositeKeys`、`{Contract}CompositeCatalog`、`BuildRoot()` 与 `BuildForest()`。

```csharp
[CompositePart<IMenuNode>("root")]
public sealed class HomeMenu : IMenuNode, ICompositeBuildable<IMenuNode> { ... }

[CompositePart<IMenuNode>("admin", Order = 5)]
public sealed class AdminMenu : IMenuNode, ICompositeBuildable<IMenuNode> { ... }

var forest = MenuNodeCompositeCatalog.BuildForest();
CompositeTraverser.TraverseForest(forest, (node, depth, rootIndex) => { ... });
```

| API | 适用场景 |
|-----|----------|
| `BuildRoot()` | catalog 中**恰好一个** `ParentKey == null` |
| `BuildForest()` | **一个或多个**根(按 `Order` 再 key 排序) |

多根 catalog 调用 `BuildRoot()` 会在运行时抛 `CompositeAssemblyException`。

## 诊断

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)
2 changes: 1 addition & 1 deletion docs/zh/samples.md
Original file line number Diff line number Diff line change
Expand Up @@ -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** | 装饰器栈 |
Expand Down
Loading