I was very excited about the Decorator Pattern, right up until I wasn’t.
Being new to design patterns, and OO programing, and Java, and programming in general, the Decorator Pattern looked like an effective (and entertaining) solution to a particular challenge: applying effects (i.e. feats, buffs, equipment bonuses) to actors in order to change their properties (i.e. ability scores, to hit, AC). Also known as a ‘Wrapper’, this pattern “… allows behavior to be added to an individual object… without affecting the behavior of other objects from the same class.”
Perfect! So, if the hero casts Bless, for example, just wrap her in a Decorator like this:
// Abstract decorator class for Hero public abstract class HeroDecorator implements HeroInterface { protected final Hero decoratedHero; public HeroDecorator(Hero hero) { this.decoratedHero = hero; } // Implement interface method public int getToHit() { return decoratedHero.getToHit(); } } // Decorator wraps the Hero in a Bless spell class Bless extends HeroDecorator { public Bless(Hero hero) { super(hero); } // Overriding method defined in abstract superclass public int getToHit() { return super.getToHit() + 1; // +1 to hit bonus from spell } }
To ‘Bless’ the hero, just do this: hero = new Bless(hero);
. Now hero.getToHit()
will return the original ‘to hit’ with a +1 bonus. Done! If the hero picks up a magic sword that adds another +1 to ‘to hit’, hero = new MagicSword(hero);
where class MagicSword extends HeroDecorator
. If the hero becomes cursed, and takes a penalty on ‘to hit’, you can still do hero = new Curse(hero);
. Decorators can also be stacked, so you can just keep wrapping and wrapping. hero = new Bless(new MagicSword(new Curse(hero)));
will work.
There are reasons, however, why all of this is a bad idea.
Removing the frosting
The first problem with the Decorator Pattern is that once you wrap an object you cannot unwrap it, at least not without a hack. The Bless spell above is eventually going to expire, at which point the hero no longer gets the bonus, so what do you do?
One option is to implement a way to ‘deactivate’ the decoration. Perhaps the Decorator itself would have a timer that, after a certain duration, would stop adding the bonus. Or perhaps you could implement a method enabling the toggle of a boolean active;
within the Decorator. Regardless, this is going to be a terrible approach as the object will continually grow in size, forever, as it’s wrapped, re-wrapped and wrapped again. Imagine what your hero will look like by level 20.
Generally, you cannot remove the wrapping. According to this amazing post on StackOverflow, “You cannot un-decorate a function. (There are hacks to create decorators that can be removed, but nobody uses them.) So once a function is decorated, it’s decorated for all the code.” The ‘hack’ being referred to here essentially involves treating the Decorators like a Linked List: each Decorator keeps track of both the immediately outer Decorator, in addition to the immediately inner Decorator, so that when it expires it can ‘decouple’ itself by linking the two of them.
Keeping track of the ingredients
Nevertheless, even if you implement the hack for ‘decoupling’ Decorators you are probably going to still have the headache of keeping track of all of the decorated methods. Notice the HeroInterface
above. It looks like this:
public interface HeroInterface { public int getToHit(); // Returns d20 to hit roll }
It exists to make sure that any decorated method gets ‘passed through’ all of the layers of Decorators so it can end up at the base object. This means that every Decorator, even those that do not affect ‘to hit’ (e.g. override public int getToHit()
), must implement the method and pass along the functionality to the super method, like public int getToHit() { return super.getToHit(); }
. Furthermore, every decorated method needs to be in the interface, which could end up looking like this:
public interface HeroInterface { public int getToHit(); // Returns d20 to hit roll public int getAC(); // Returns AC public int getAbility(Abilities ability); // Returns Str, Int, etc. public int getSavingThrow(Saves save); // Returns saving throw // and on, and on, and on... }
Every Decorator will then have to implement every one of these methods, which will get ugly. Add a decorated method and you’ll have to then update every Decorator. According to Michael Feldman, “For longer classes, a programmer must choose the lesser of two evils: implement many wrapper methods and keep the type of decorated object or maintain a simple decorator implementation and sacrifice retaining the decorated object type.” In other words: hassle.
Remarkably, however, this, too, can be ‘hacked’. This StackOverflow question describes the exact problem of a implementing an interface for a class with lots of methods to decorate. My answer involves eliminating the interface by implementing the bytecode generation library, CGLIB. Using a proxy solution Decorators are then able to implement just the methods they want to override while simply passing through everything else. There’s a performance hit, but it works. You can try my code for yourself.
Eat ice cream instead
While with a Linked List style Decorator and a bytecode generation library proxy solution you could, in the end, decorate your cake and eat it, too (you’re welcome), there’s a better way.
As helpfully pointed out in a StackOverflow chat room, Anubian Noob suggested storing the ‘to hit’ bonus (and all other parameters that could be affected by an effect) in the base class, loading all of the effects into a list and simply iterating through all of them during updates. My Bless class now looks something like this:
public class Bless extends Effects { private Actor actor; public Bless(Actor actor) { this.actor = actor; } @Override public update() { actor.toHit += 1; } }
When the Bless is complete, just remove it from List<Effects> effects = new ArrayList<>();
. Easy. Alternatively, which is what I chose to do, you can iterate through the collection of Effects whenever a value, such as ‘to hit’, is required. It looks something like this:
public class Hero { private List<Effects> effects = new ArrayList<>(); private int toHitBonus = 0; private Random rand = new Random(); public int getToHit() { for (Effects effect : effects) effect.updateValues(); return rand.nextInt(20) + 1 + toHitBonus; } }
As one of the design patterns laid out in the Gang of Four’s famous tome, the Decorator Pattern must certainly have important utility. At least for me, however, despite no lack of effort, I decided that it just wasn’t helpful in this case.