-
Notifications
You must be signed in to change notification settings - Fork 16
Decorator Pattern
Wikipedia's defintion of the decorator pattern:
In object-oriented programming, the decorator pattern is a design pattern that allows behavior to be added to an individual object, dynamically, without affecting the behavior of other instances of the same class. The decorator pattern is often useful for adhering to the Single Responsibility Principle, as it allows functionality to be divided between classes with unique areas of concern as well as to the Open-Closed Principle, by allowing the functionality of a class to be extended without being modified. Decorator use can be more efficient than subclassing, because an object's behavior can be augmented without defining an entirely new object.
Decorator Pattern is excellent for dynamically modifying or enhancing game stats like adding buffs or debuffs to a character’s base stats
1, Define a basic decorator (BaseHealth
) and a couple of stat decorators (HealthBuff
and HealthDebuff
) that will alter the health stat.
using Pancake.Pattern;
public class BaseHealth : IDecorator<float>
{
private readonly float _baseValue;
public BaseHealth(float baseValue) { _baseValue = baseValue; }
public float Operation() { return _baseValue; }
}
using Pancake.Pattern;
public class HealthBuff : IDecorator<float>
{
private readonly float _buffPercentage;
private readonly IDecorator<float> _baseStat;
public HealthBuff(IDecorator<float> baseStat, float buffPercentage)
{
_baseStat = baseStat;
_buffPercentage = buffPercentage;
}
public float Operation() { return _baseStat.Operation() * (1 + _buffPercentage); }
}
using Pancake.Pattern;
public class HealthDebuff : IDecorator<float>
{
private readonly float _debuffPercentage;
private readonly IDecorator<float> _baseStat;
public HealthDebuff(IDecorator<float> baseStat, float debuffPercentage)
{
_baseStat = baseStat;
_debuffPercentage = debuffPercentage;
}
public float Operation() { return _baseStat.Operation() * (1 - _debuffPercentage); }
}
2, Create a Concrete HealthStat
Class
Define a HealthStat
class that extends Decorable<float>
and provides a default implementation for handling health.
using Pancake.Pattern;
public class HealthStat : Decorable<float>
{
}
3, Now We apply decorators to modify the health stat dynamically using the Decorable<float>
class.
using Pancake.Pattern;
using UnityEngine;
public class Character : MonoBehaviour
{
private void Start()
{
// Initialize base health
IDecorator<float> baseHealth = new BaseHealth(100f);
// Apply a health buff (e.g., +20%)
IDecorator<float> healthBuff = new HealthBuff(baseHealth, 0.2f);
// Apply a health debuff (e.g., -10%) on top of the buff
IDecorator<float> healthDebuff = new HealthDebuff(healthBuff, 0.1f);
// Use Decorable to manage the current health operation
var healthStat = new HealthStat { Decorator = healthDebuff };
// Get the final health after applying the buff and debuff
float finalHealth = healthStat.Operation();
Debug.Log($"Character's final health after buffs and debuffs: {finalHealth}");
}
}
(100 * (1 + 0.2)) * (1 - 0.1) = 100 * 1.2 * 0.9 = 108