← All patterns
Structural

Decorator

Stack extra behaviour onto an object at runtime by wrapping it in layers that share its interface.

What Is It?

A Decorator wraps an object, implements the same interface, and forwards calls to it while adding something of its own before or after. Because the wrapper looks exactly like the thing it wraps, you can wrap the wrapper too, building a chain of small, focused layers.

Weapon upgrades are the textbook game case. A BaseSword deals 10 damage. Wrap it in a FireDecorator and GetDamage() asks the sword, then adds burn. Wrap that in a CritDecorator and the whole result gets multiplied. The player’s attack code calls weapon.GetDamage() and has no idea how many layers are underneath.

This beats subclassing when combinations explode. You do not need FirePoisonCritSword; you need four small decorators and the ability to compose them in any order the loot system rolls.

When Is It Used?

Use it for stackable modifiers: weapon enchantments, buff and debuff layers, status effects that alter damage or speed, or logging and caching around a service.

It fits well when modifiers are picked at runtime from data (loot tables, perks, item sockets) rather than known at compile time.

Avoid it when the order of layers must be strictly controlled or when you need to inspect and remove a specific inner layer often. A flat list of modifiers processed by a single calculator is usually simpler for stat systems.

Interactive Demo

Click the controls and watch the objects collaborate. The console mirrors what the C# code below would log.

Code

Assets / Scripts / Structural/ Decorator ›WeaponTester.cs
using UnityEngine;

namespace Patterns.Structural.Decorator
{
    /// <summary>
    /// Builds a weapon by wrapping a base sword in decorators,
    /// then attacks through the whole chain with a single call.
    /// </summary>
    public class WeaponTester : MonoBehaviour
    {
        private IWeapon weapon;

        private void Start()
        {
            weapon = new BaseSword();
            weapon = new FireDecorator(weapon);    // +5 burn
            weapon = new PoisonDecorator(weapon);  // +3 poison
            weapon = new CritDecorator(weapon);    // x2 outer multiplier

            Debug.Log(weapon.Describe()); // Sword + Fire + Poison + Crit
        }

        private void Update()
        {
            if (Input.GetKeyDown(KeyCode.Space))
                Debug.Log($"Hit for {weapon.GetDamage()}"); // (10 + 5 + 3) * 2 = 36

            if (Input.GetKeyDown(KeyCode.F))
                weapon = new IceDecorator(weapon); // stack another layer at runtime
        }
    }
}
4 files · namespace Patterns.Structural.DecoratorC# · UTF-8 · LF

Advantages & Disadvantages

+ Advantages

  • Behaviour is added without modifying the wrapped class or creating subclass explosions.
  • Layers compose freely and can be added or peeled off at runtime.
  • Each decorator does one thing, so it is easy to test in isolation.

− Disadvantages

  • Order matters: a crit multiplier applied before or after flat fire damage gives different results, which can surprise designers.
  • Removing a layer from the middle of the chain is awkward; you usually rebuild the chain.
  • Many tiny wrapper objects make stack traces and debugging noisier.

Tips

  1. 01Put shared forwarding in an abstract WeaponDecorator base so concrete decorators only override what they change.
  2. 02Decide and document an ordering convention (flat bonuses first, multipliers last) and enforce it when building chains.
  3. 03Expose a Describe() method that each layer appends to; it makes tooltips and debugging trivial.
  4. 04Decorators are plain C# objects; keep them out of MonoBehaviours so they are cheap to create when an item is equipped.
  5. 05For heavy stat systems with dozens of sources, consider a modifier list instead, and keep decorators for behavioural changes like on-hit effects.