Listen to this post
Attach additional responsibilities to an object dynamically. Decorators provide a flexible alternative to sub-classing for extending functionality. (GoF)
Processing an image for display or to send to a printer can involve many different manipulations depending on the effect desired and the capabilities (such as color gamut) of the target device.
These manipulations can include spatial passes, Fourier transformations, affine transformations, and so forth. Each does something different to alter the way the image appears. Once filtered, the image is displayed or printed.
An image processing system should allow for none, one, some, or all of these manipulations to be applied, and in any order desired by the end-user.
Qualities and principles
Each decorator does a single kind of decoration, making them strongly cohesive. The clients couple only to the interface of the abstraction (ImageOut in the example). Which decorators are in use, the order in which they operate, and the final destination are all encapsulated from clients, and thus all are open-closed. The interface comes from no particular implementation, but from the clients’ needs.
Each decorator can be tested using a mock of the decorated object.
Questions and concerns
Ideally, all decorators and the decorated objects should have the same interface to allow for maximum flexibility. An Adapter can be used when this is not true. Decorator is typically implemented using a Template Method to separate the decorating behavior from the common hand-off-to-the-next behavior.
Decorator chains nearly always have business rules regarding which combination are permissible and which are not, including constraints on sequence and which decorators can and cannot be used together. Because of this, some kind of factory is typically used to capture and enforce these restrictions and to separate them from client concerns.