Skip to content

Generator: Create Decorator Pattern #36

@JerrettDavis

Description

@JerrettDavis

Summary

Add a source generator that produces boilerplate-free, GoF-consistent Decorator pattern implementations for interfaces and abstract classes.

The generator lives in PatternKit.Generators and emits self-contained, readable C# with no runtime PatternKit dependency.

Primary goals:

  • Generate typed decorators without reflection.
  • Make ordering explicit and deterministic.
  • Support both “wrapper class” style and “pipeline” (next) style.
  • Support sync + async members (favor ValueTask for generated hooks).

Motivation / Problem

Decorator chains are frequently hand-written and drift:

  • missing forwarding members
  • inconsistent ordering semantics
  • repeated logging/timing/retry boilerplate
  • async gets messy quickly

Source generation can emit correct forwarding + consistent hooks once.


Supported Targets (must-have)

Decorator generation must support:

  • interface
  • abstract class (virtual/abstract members only)

Decorator generation is opt-in via attribute on the contract.


Proposed User Experience

A) Generate base decorator + optional pipeline builder

[GenerateDecorator]
public partial interface IStorage
{
    Stream OpenRead(string path);
    ValueTask<Stream> OpenReadAsync(string path, CancellationToken ct = default);
}

Generated (representative shape):

public abstract partial class StorageDecoratorBase : IStorage
{
    protected StorageDecoratorBase(IStorage inner);

    protected IStorage Inner { get; }

    public virtual Stream OpenRead(string path) => Inner.OpenRead(path);
    public virtual ValueTask<Stream> OpenReadAsync(string path, CancellationToken ct = default)
        => Inner.OpenReadAsync(path, ct);
}

public static partial class StorageDecorators
{
    public static IStorage Compose(IStorage inner, params Func<IStorage, IStorage>[] decorators);
}

B) Typed decorators written by users

public sealed class CachingStorage : StorageDecoratorBase
{
    public CachingStorage(IStorage inner) : base(inner) { }

    public override Stream OpenRead(string path) { /* cache */ }
}

The generator ensures the base class always stays in sync with the contract.


Attributes / Surface Area

Namespace: PatternKit.Generators.Decorator

  • [GenerateDecorator] on contract type

    • string? BaseTypeName (default: <ContractName>DecoratorBase)
    • string? HelpersTypeName (default: <ContractName>Decorators)
    • DecoratorCompositionMode Composition (default: HelpersOnly)
    • bool GenerateAsync (default: inferred)
    • bool ForceAsync (default: false)

Optional:

  • [DecoratorIgnore] for members to exclude.

Enums:

  • DecoratorCompositionMode: None, HelpersOnly, PipelineNextStyle (v2)

Semantics (must-have)

Forwarding correctness

  • Base decorator forwards all included members to Inner.
  • For abstract class contracts, only virtual/abstract members are forwarded.

Composition helper ordering

If helper Compose is generated:

  • decorators applied in array order.

  • ordering must be documented:

    • decorators[0] is outermost by default.

Async

  • Base decorator preserves exact signature from contract.
  • No signature rewriting.

Diagnostics (must-have)

  • PKDEC001 Type marked [GenerateDecorator] must be partial.
  • PKDEC002 Unsupported member kind (e.g., event) for v1.
  • PKDEC003 Name conflict for generated base/helper types.
  • PKDEC004 Member not accessible for generation.

Generated Code Layout

  • ContractName.Decorator.g.cs

Determinism:

  • stable emission order by fully-qualified member name.

Testing Expectations

  • Base decorator forwards calls correctly.
  • Composition helper wraps in deterministic order.
  • Async forwarding works.
  • Diagnostics: unsupported members, naming conflicts.

Acceptance Criteria

  • [GenerateDecorator] works for interfaces and abstract classes.
  • Generated base decorator forwards all members correctly.
  • Optional composition helpers generated with deterministic ordering.
  • Async members supported without signature rewriting.
  • Diagnostics and tests included.

Metadata

Metadata

Labels

enhancementNew feature or request

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions