Recently I read about the decorator design pattern but I was left with some unanswered questions that I couldn't find online. I'm not going to show code because I don't want to make this question more complicated than it really is. I'll simply give an example:
Component --> SubSandwich
ConcreteComponent --> 15cmSub, 30cmSub
Decorator --> Ingredients
ConcreteDecorator --> White Cheese, Yellow Cheese, Jam, Chicken.
This is just the way a Subway store works. Pick your core sandwhich size and then add all the toppings you like. But I still have some questions:
- What if there is an invalid combination of ingredients? For example, Subway policy says that there can't be two kinds of cheeses in the same sub.
Then the Decorator pattern is probably not what you're looking for. Decorator is about composing behavior without changing interface. It is not about concatenating attributes, and it doesn't work well for that because only the outermost decorator instance is (usually) directly available to the user. If you need somehow to know whether a
SubWithProvolone wraps a
SubWithTurkey -- or any other particular kind of sub -- then you're doing it wrong.
The best example I know is exhibited by the Java I/O classes
Writer and all their subclasses. Any
InputStream can be made buffered by decorating it with a
InputStream can be used as a
Reader by decorating it with an
But if you started throwing in rules such as "a
BufferedWriter must not be decorated by another
BufferedWriter" (hypothetical) then it breaks -- there's just no good way to enforce it, and in truth, there's no bona fide need for the rule. If you are trying to model a situation where there is a true need for rules like that about how components can be composed, then Decorator is not a very good fit.
- What if two ingredients are dependant. For example, if you order lettuce THEN you need some other kind of vegetable to make a "valid" Sub.
This is essentially the same question as #1. You could probably kludge something together to solve this problem, and the previous one, but if such problems present themselves at all then Decorator is not a good fit for the situation.
- When is it better to use the decorator pattern instead of a SubSandwich class with an ArrayList of Ingredient? I know here the Ingredients don't add behaviour, which makes the Subway example not accurate, but let's assume they do.
The other aspects of your question are a bit broad for SO, but this one is far too broad. We answer specific questions about programming details.
- Why extending? Why not using interfaces?
You inquire about a false dichotomy. That Java distinguishes between interfaces and pure abstract classes is a superficial language-design detail that serves mainly to support Java's restriction to single inheritance of implementation. There is no deep difference between interfaces and pure abstract classes that would survive if your question were redirected to C++ instead, to which design patterns are equally relevant. You can implement the Decorator pattern around a base type that is a Java interface.
With that said, the Java I/O classes I already mentioned do use classes rather than interfaces for the base type. There are both advantages and disadvantages, but one of the advantages is that it allows them to use the Template Method pattern to provide for easier implementation of concrete implementation classes. Even there, however, abstract base classes could have been provided for that purpose even if the decorator type were declared as an interface.