- 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 InputStream
, OutputStream
, Reader
, and Writer
and all their subclasses. Any InputStream
can be made buffered by decorating it with a BufferedInputStream
; any InputStream
can be used as a Reader
by decorating it with an InputStreamReader
; etc..
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.