I’m a huge fan of component based UI development – the idea of having individual functional things in a UI be an encapsulated “component”. Have a search form? That’s a component. The search results? A separate component. That menu system? You guessed it… a component. There are a lot of benefits to doing things this way. But there are also a lot of challenges to doing things this way. One of those challenges is adding functionality to a model that is shared between components, when only one of those components needs that functionality.
If a single UI component needs to add functionality to a model, but no other component needs that functionality, you shouldn’t be adding that code to the shared model. Heading down that path leads to far too many concerns in the model. You’ll end up with multiple chunks of code potentially in conflict, doing similar things slightly differently, all to make different UI components and behaviors happy.
In object-oriented programming, the decorator pattern is a design pattern that allows behavior to be added to an individual object, either statically or dynamically, without affecting the behavior of other objects
(yes, that’s an “interior decorator”, decorating code :D)
Decorating With A Memento
In my current client project, I have to load up a model and allow it to be edited. Once edits are done they can be saved back to the server. When edits are incomplete, or when the user realizes they don’t want the edits, a cancel button can roll back the changes and close the form without saving anything. This is a problem I solved a long time ago with the Backbone.Memento plugin.
As I said above, I don’t want to include the memento features in the model directly. I want to decorate the model with the memento features only within the “edit” UI component. This will give me the ability to save / rollback the changes in the model, but also keep that save / rollback code isolated to the edit form – no other code in my app will know about this ability, and my model will stay clean and clear of this code.
It starts with a simple model:
Next, up I have a View and a function to decorate my model with the Memento functionality.
When the view is instantiated, I apply the decorator to the model and tell the view to hang on to the decorated object.
Now my view can correctly use the Memento methods associated with the cancel button. When someone clicks save, the changes are left in place and the view is closed. But when someone clicks cancel, the changes are rolled back.
Because this View is using a decorator to get the Memento in place, no other code has access to the Memento methods. Any code that uses the model before or after the edit view has been shown, will only have the standard model methods available.
Sometimes The Right Thing, Not Always
This technique is tremendously useful for more than just save / cancel functionality, too. It’s quite common for validation to be UI specific, for example. One UI will have a model that needs 2 or 3 fields, the next UI will have the same model but need 2 or 3 different fields, etc. Or there may be two very unrelated UI views that both use the same model based on simple variations (admin vs non-admin user, for example). In these cases, validation is going to be specific to the UI and not generic to the model. A decorator can easily add the correct validation code to the model, for the specific view.
While the decorator pattern is useful when you need to isolate behavior, it is not always the right thing to do. Sometimes the behavior really should exist inside of the model (or whatever the object is). When dealing with behavior that is specific to a UI, though, a decorator is a great way to add behavior as needed and not have to think about it in any other context.