Ayende @ Rahien

Hi!
My name is Oren Eini
Founder of Hibernating Rhinos LTD and RavenDB.
You can reach me by phone or email:

ayende@ayende.com

+972 52-548-6969

, @ Q c

Posts: 18 | Comments: 65

filter by tags archive

Design patterns in the test of timeDecorator

time to read 9 min | 1606 words

The decorator pattern is a design pattern that allows behaviour to be added to an existing object dynamically.

More about this pattern.

I don’t have a lot to say about this pattern.

Hipster Ariel - Decorator? Used it before it was cool

The decorator pattern is still just as useful if not more so as the time it was initially announced. It forms a core part of many critical function of your day to day software.

The most obvious example is the notion of Stream, where you often decorate a stream (Buffered Stream, Compressing Stream, etc). This example is valid not just for the .NET framework, I can’t think of a single major framework that doesn’t use the Stream abstraction for its IO.

Then again, the cautionary side, people try to use it in… strange ways:

   1: public abstract class CondimentDecorator : Beverage {}
   2:  
   3: public class Mocha : CondimentDecorator
   4: {
   5:     private Beverage m_beverage;
   6:  
   7:     public Mocha(Beverage beverage)
   8:     {
   9:         this.m_beverage = beverage;
  10:     }
  11:  
  12:     public override String Description
  13:     {
  14:         get
  15:         {
  16:             return m_beverage.Description + ", Mocha";
  17:         }
  18:     }
  19:  
  20:     public override double Cost()               
  21:     {                                      
  22:         return 0.20 + m_beverage.Cost();
  23:     }
  24: }

Something like this, for example, is a pretty bad way of doing things. It assumes a very fixed model of looking at the universe, which is just not true. Decorator works best when you have just a single I/O channel. When you have multiple inputs & outputs, decorating something becomes much harder.

In particular, implementing business logic like the one above in decorators make it very hard to figure out why things are happening. In particular, CachingDecorator is something that you want to avoid (better to use infrastructure or a auto caching proxy, instead).

Recommendation: It is a very useful pattern, and should be used when you actually have one input / output channel, because that is a great way to allow to dynamically compose the way we apply processing to it.

More posts in "Design patterns in the test of time" series:

  1. (21 Jan 2013) Mediator
  2. (18 Jan 2013) Iterator
  3. (17 Jan 2013) Interpreter
  4. (21 Nov 2012) Command, Redux
  5. (19 Nov 2012) Command
  6. (16 Nov 2012) Chain of responsibility
  7. (15 Nov 2012) Proxy
  8. (14 Nov 2012) Flyweight
  9. (09 Nov 2012) Façade
  10. (07 Nov 2012) Decorator
  11. (05 Nov 2012) Composite
  12. (02 Nov 2012) Bridge
  13. (01 Nov 2012) Adapter
  14. (31 Oct 2012) Singleton
  15. (29 Oct 2012) Prototype
  16. (26 Oct 2012) Factory Method
  17. (25 Oct 2012) Builder
  18. (24 Oct 2012) A modern alternative to Abstract Factory–filtered dependencies
  19. (23 Oct 2012) Abstract Factory

Comments

Jason Young

Is that code example from Head First Design Patterns? I found that book's examples to not always be very motivating--that is, they demonstrated use of a pattern, but not necessarily a case where you'd actually want to use that pattern.

Michal Lašák

Could you please specify more, why we should avoid CachingDecorator? Actually I thought that it was very elegant solution to do caching and work with DI container.

Euphoric

@Michal : I think one reasons are side effects. This decorator would need to know if underlying data/decorated object changed and invalidate itself. This introduces hard to debug problems when cache decorator returns invalid data. I think this is what having one input/output channel means.

Jacob T

ditto re the CachingDecorator. What is wrong with it?

Chris

Decorator plays especially nicely with DI containers and cross cutting concerns. StructureMap's Interceptor API just screams decorator at you.

Ayende Rahien

Euphoric, That is certainly one aspect of that. The other aspect is that when you add a new method to the decorated object, you now need to go and add that to all the decorating, etc. I don't like this sort of behavior.

Stan

Decorator can be usefull in c++ with its multiple inheritance. For c# it's mostly useless for reasons Ayende have provided. Something like dynamic proxy or post sharping may be better used for injecting behaviour.

Frisian

@Ayende: That's exactly one of the differences between Proxy and Decorator: In a decorator behaviour is added with the API of the underlying object in mind. So if that API changes, the decorator has to be modified as well. In a proxy on the other hand, you have a one-size-fits-all approach, i.e. adding the same functionality to every method of the underlying object. Consequently, proxies can be constructed dynamically in many languages, usually employing reflection.

Stan

Stan: I'm not sure how dynamic proxy or using PostSharp is different. OK, you don't write decorator by hand but still something is decorating object. Runtime behaviour is same.

Comment preview

Comments have been closed on this topic.

FUTURE POSTS

  1. RavenDB 3.0 New Stable Release - 7 hours from now
  2. Production postmortem: The case of the lying configuration file - about one day from now
  3. Production postmortem: The industry at large - 2 days from now
  4. The insidious cost of allocations - 3 days from now
  5. Buffer allocation strategies: A possible solution - 6 days from now

And 4 more posts are pending...

There are posts all the way to Sep 11, 2015

RECENT SERIES

  1. Find the bug (5):
    20 Apr 2011 - Why do I get a Null Reference Exception?
  2. Production postmortem (10):
    31 Aug 2015 - The case of the memory eater and high load
  3. What is new in RavenDB 3.5 (7):
    12 Aug 2015 - Monitoring support
  4. Career planning (6):
    24 Jul 2015 - The immortal choices aren't
View all series

Syndication

Main feed Feed Stats
Comments feed   Comments Feed Stats