[← Design patterns index](README.md) # Decorator **Category:** Structural | **Also known as: Wrapper** | **Source:** `patterns/decorator/ShapeDecorations/` ## Problem You want to add behavior to _individual objects_ at runtime, not to every instance of a class, without an explosion of subclasses for every combination (`BorderedScrollableTextView`, `BorderedTextView`, …). Inheritance is also static: once an object is constructed you cannot add or remove an inherited behavior. ## Solution Wrap the object in a decorator that **implements the same interface** and **holds a reference** to what it wraps. The decorator delegates to the wrappee and adds its own behavior before or after. Because a decorator is itself the component type, decorators wrap decorators — you build a stack, and the outermost object stays indistinguishable from the original to every caller. ## Participants (GoF names → this repo) - **Component** → `IShape` - **ConcreteComponent** → `Circle`, `Rectangle` - **Decorator** → `ShapeDecorator` (abstract base holding the wrappee) - **ConcreteDecorator** → `RedColor`, `BlueBorder`, `HugeSize` ```mermaid classDiagram class IShape { <> +Draw() } class ShapeDecorator { <> #IShape _decorated +Draw()* } IShape <|.. Circle IShape <|.. Rectangle IShape <|.. ShapeDecorator ShapeDecorator o--> IShape : wraps - recursive ShapeDecorator <|-- RedColor ShapeDecorator <|-- BlueBorder ShapeDecorator <|-- HugeSize ``` `ShapeDecorator` both **implements** `IShape` and **holds** an `IShape`. That double relationship is the whole pattern — it is what makes wrapping recursive. ## Code ```csharp // IShape.cs — Component public interface IShape { void Draw(); } // Circle.cs / Rectangle.cs — ConcreteComponents (the only classes that can start a stack) public class Circle : IShape { public void Draw() { Console.WriteLine("Drawing a circle"); } } public class Rectangle : IShape { public void Draw() { Console.WriteLine("Drawing a Rectangle"); } } ``` ```csharp // ShapeDecorator.cs — base Decorator (doc comments condensed from the original) public abstract class ShapeDecorator : IShape { /// Maintains a reference to the shape which it decorates. /// Marked "protected" so that inheriting classes have access to it. protected IShape _decorated; /// Accepts a reference to any IShape. Since the ShapeDecorator itself IS an /// IShape, this decorator can also decorate other decorators. However, since a /// ShapeDecorator has to be instantiated with an existing IShape, at some point /// in the chain there must be a concrete shape. public ShapeDecorator(IShape decorated) { _decorated = decorated; } /// Abstract: satisfies IShape, but forces subclasses to write their own logic. public abstract void Draw(); } ``` ```csharp // RedColor.cs — ConcreteDecorator (BlueBorder and HugeSize are the same shape) public class RedColor : ShapeDecorator { public RedColor(IShape decorated) : base(decorated) { } public override void Draw() { _decorated.Draw(); // 1. delegate inward Console.WriteLine("\tWhich is red in color"); // 2. then add own behavior } } // BlueBorder.Draw() -> _decorated.Draw(); "\tWith a blue border" // HugeSize.Draw() -> _decorated.Draw(); "\tThat is enourmous in size" ``` ```csharp // Program.cs — client assembles the stack; each variable remains an IShape var circle = new Circle(); circle.Draw(); var red = new RedColor(circle); // Circle → RedColor red.Draw(); var blueBorder = new BlueBorder(red); // Circle → RedColor → BlueBorder blueBorder.Draw(); var huge = new HugeSize(blueBorder); // Circle → RedColor → BlueBorder → HugeSize huge.Draw(); // Second block: same decorators, different order, different concrete component var rect = new Rectangle(); var blueBorder2 = new BlueBorder(rect); var huge2 = new HugeSize(blueBorder2); var red2 = new RedColor(huge2); red2.Draw(); ``` ## Output (verified) ``` Drawing a circle Drawing a circle Which is red in color Drawing a circle Which is red in color With a blue border Drawing a circle Which is red in color With a blue border That is enourmous in size Drawing a Rectangle Drawing a Rectangle With a blue border Drawing a Rectangle With a blue border That is enourmous in size Drawing a Rectangle With a blue border That is enourmous in size Which is red in color ``` Read the output as the call stack unwinding: because every decorator calls `_decorated.Draw()` **first**, the innermost concrete shape prints first and decorations print outward in wrapping order. The `Program.cs` comment says _"the order in which we decorate doesn't matter"_ — true for _these_ decorators, because each only appends an independent line. It is **not** true in general (see Cons). ## GoF pseudocode (compression + encryption over a data source) ``` interface DataSource is method writeData(data) method readData(): data class FileDataSource implements DataSource is // ConcreteComponent method writeData(data) is // write to file method readData(): data is // read from file class DataSourceDecorator implements DataSource is // base Decorator protected field wrappee: DataSource constructor DataSourceDecorator(source) is wrappee = source method writeData(data) is wrappee.writeData(data) method readData(): data is return wrappee.readData() class EncryptionDecorator extends DataSourceDecorator is method writeData(data) is // encrypt, then wrappee.writeData(...) method readData(): data is // wrappee.readData(), then decrypt class CompressionDecorator extends DataSourceDecorator is method writeData(data) is // compress, then wrappee.writeData(...) method readData(): data is // wrappee.readData(), then decompress // the stack is assembled from configuration, at runtime source = new FileDataSource("salary.dat") if (enabledEncryption) source = new EncryptionDecorator(source) if (enabledCompression) source = new CompressionDecorator(source) ``` Note this example's decorators _are_ order-sensitive: compress-then-encrypt and encrypt-then-compress give different bytes (and wildly different compression ratios). ## Applicability - Add responsibilities to individual objects dynamically and transparently, without affecting other objects. - The responsibilities can be withdrawn again. - Extension by subclassing is impractical: too many independent combinations, or the class is `sealed`/`final` and can't be subclassed at all. ## Pros / Cons | Pros | Cons | | ----------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------- | | Extend behavior with no new subclass | Hard to remove one specific wrapper from the middle of a stack | | Add/remove responsibilities at runtime | Hard to keep behavior independent of stack order | | Combine behaviors by stacking | Setup code that assembles the stack gets ugly | | Single Responsibility — one class per behavior | A decorated object is **not** identical to the original — don't rely on reference identity or `GetType()` | | Easy to apply the _same_ decorator twice (for example, a double border) | Yields lots of small look-alike objects; harder to debug | ## Seen in the wild (.NET) - `System.IO.Stream` is the canonical case: `GZipStream`, `CryptoStream`, and `BufferedStream` each wrap another `Stream` and are themselves a `Stream` — exactly the GoF pseudocode shown earlier. - ASP.NET Core middleware. - `IMemoryCache`/`HttpMessageHandler` wrappers. ## Related patterns - **Adapter** changes an interface; Decorator keeps it and adds behavior — and supports recursive composition, which Adapter doesn't. - **Composite** — similar recursive-composition diagram, but a Decorator has exactly one child and _adds responsibilities_, while Composite _sums up_ many children. You can decorate a single node inside a Composite tree. - **Strategy** — Decorator changes an object's _skin_; Strategy changes its _guts_. - **Proxy** — same structure, different intent: a Proxy manages its subject's lifetime and access; a Decorator's composition is always driven by the client. - **Chain of Responsibility** — also recursive, but handlers may _stop_ the request; decorators must never break the flow. --- **← Prev:** [Bridge](bridge.md) · [All patterns](README.md) · **Next →** [Facade](facade.md)