Search code examples
javadesign-patternsumldecoratorsoftware-design

Decorator Pattern with added functions


I found this example for the decorator pattern in a text book.

Decorator pattern example

If we considered the object:

LockedStack MyStack = new LockedStack(new UndoStack(new Stack()))

How can I call the function undo on it?

MyStack.delegate.undo() won't work because delegate is private.

Does this example not work in this situation or am I missing something?


Solution

  • The purpose of the decorator pattern is:

    to add new reponsibilities to objects dynamically.

    If the new responsibility is embedded in the existing operations (e.g. push() and pop()), the decorator will take care of forwarding the call to the decorated object before/after performing the additional responsibilities. It is then straightforward to chain decorators. You could for example achieve this with a slightly different design of the secure stack in which cryptographic methods would be private, and pushing/poping would secure the content on the flow:

    enter image description here

    If the new responsibilities are about additional operations, the design is not wrong (in fact GoF provides such an example). But it's another story when chaining decorators:

    • The decorator pattern does not provide any way to cumulate the new operations of all the intermediary decorators along the line. (If you really need this, consider rather the entity component system pattern)
    • The decorator pattern does not either provide an access to the decorated object, since this would lead client code to break the principle of least knowledge (law of demeter). Moreover you'd need to use risky down-castings.

    The solution is then to use your decorator chain in another way: if you need a LockStack that offers lock operations and an UndoStack that offers undo, you'll need to keep a track of each of these decorator objects, and use the right object to obtain the corresponding operations:

    UndoStack MyUndoStack = new UndoStack(new SecureStack (key,new Stack()));
    LockedStack MyLockStack = new LockedStack(MyUndoStack); 
    MyLockStack.lock();
    MyUndoStack.undo();
    MyLockStack.unlock();
    

    This its less elegant but far more robust than creating a delegate getter and assuming in some code that a lockstack allways delegates to an undostack (hidden coupling).