decorator.md

July 3, 2026 ยท View on GitHub

Decorator

Interception is the ability to intercept calls between objects in order to enrich or change their behavior, but without having to change their code. A prerequisite for interception is weak binding. That is, if programming is abstraction-based, the underlying implementation can be transformed or improved by "packaging" it into other implementations of the same abstraction. At its core, intercept is an application of the Decorator design pattern. This pattern provides a flexible alternative to inheritance by dynamically "attaching" additional responsibility to an object. Decorator "packs" one implementation of an abstraction into another implementation of the same abstraction like a "matryoshka doll". Decorator is a well-known and useful design pattern. It is convenient to use tagged dependencies to build a chain of nested decorators, as in the example below:

using Shouldly;
using Pure.DI;

DI.Setup(nameof(Composition))
    .Bind("base").To<TextWidget>()
    .Bind().To<BoxWidget>()
    .Root<IWidget>("Widget");

var composition = new Composition();
var widget = composition.Widget;
widget.Render().ShouldBe("[ Hello World ]");

interface IWidget
{
    string Render();
}

class TextWidget : IWidget
{
    public string Render() => "Hello World";
}

class BoxWidget([Tag("base")] IWidget baseWidget) : IWidget
{
    public string Render() => $"[ {baseWidget.Render()} ]";
}
Running this code sample locally
dotnet --list-sdk
  • Create a net10.0 (or later) console application
dotnet new console -n Sample
dotnet add package Pure.DI
dotnet add package Shouldly
  • Copy the example code into the Program.cs file

You are ready to run the example ๐Ÿš€

dotnet run

Here an instance of the TextWidget type, labeled "base", is injected in the decorator BoxWidget. You can use any tag that semantically reflects the feature of the abstraction being embedded. The tag can be a constant, a type, or a value of an enumerated type.

The following partial class will be generated
partial class Composition
{
  public IWidget Widget
  {
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    get
    {
      return new BoxWidget(new TextWidget());
    }
  }
}

Class diagram:

---
 config:
  class:
   hideEmptyMembersBox: true
---
classDiagram
	TextWidget --|> IWidget : "base"
	BoxWidget --|> IWidget
	Composition ..> BoxWidget : IWidget Widget
	BoxWidget *-- TextWidget : "base" IWidget
	namespace Pure.DI.UsageTests.Interception.DecoratorScenario {
		class BoxWidget {
				<<class>>
			+BoxWidget(IWidget baseWidget)
		}
		class Composition {
		<<partial>>
		+IWidget Widget
		}
		class IWidget {
			<<interface>>
		}
		class TextWidget {
				<<class>>
			+TextWidget()
		}
	}

See also: