5

I'm reading this article:

http://www.codeproject.com/Articles/479635/UnderstandingplusandplusImplementingplusDecoratorp

I'm thinking of implementing this pattern in a school project. It's not a requirement so I can half-ass it. But, I just thought it would be a good opportunity to expand my knowledge and expertise.

The school project is this: Create a pizza ordering application where employees enter the orders of customers. So a pizza, and it can have any number of toppings.

The above article (and the description from the Head First: Design Patterns book) seem to match my application perfectly.

Here is my issue: This doesn't seem like a good pattern, and here is why:

Whenever "pizza place" adds a new topping to their menu.... they will have to add a whole new class, and recompile their ordering systems and re-distribute them?

I think perhaps the issue is that all of the examples I google have to deal with food and toppings of some sort.

  1. Am I just finding the wrong types of examples for this pattern?
  2. What are some better examples where this pattern would be implemented?
  3. Is the food industry one of them and it is just the implementations that are screwy?
  4. Is this one of those patterns that is out there but is hardly ever used in actual production code?
Mahm00d
  • 3,881
  • 8
  • 44
  • 83
Steve's a D
  • 3,801
  • 10
  • 39
  • 60
  • Related: http://stackoverflow.com/questions/6366385/decorator-pattern-for-io – BalusC Jan 25 '13 at 16:55
  • Yes, this is a good application of expanding your knowledge and expertise pertaining to the Decorator Pattern. No, if you were to implement an actual commercial pizza ordering application, you would want it to be more data-driven, not code-driven, with hooks into your website (or vice versa) so that adding/removing toppings from the menu occur "at real time" (with support for pending orders, e.g. orders placed for birthday party next week with now obsolete veggie-bacon topping). – franji1 Jan 25 '13 at 16:58
  • @franji1 how could I make this more data-driven? I was thinking of a "topping" decorator instead of individual classes for each topping and then just creating the toppings from a database as I needed them. But I feel like this would be some sort of anti-pattern – Steve's a D Jan 25 '13 at 17:01
  • Think of a text file that lists all the toppings for THAT day. Serialize your MenuTopping objects. Read up that file and present that as a list of MenuTopping objects, but this is not utilizing the Decorator pattern. However, I like lazybereovsky's answer because it lets you add other implementations of IPizza, which is just as good (if not better than) a more data-driven implementation. – franji1 Jan 26 '13 at 00:01

4 Answers4

5

Actually Decorator allows you to add some behavior without recompiling source code. You can declare IPizza interface in your pizza domain, and use this abstraction in your application. Then you can add another assembly, which will have other implementations if IPizza decorators, and inject those implementations to your application with dependency injection. Thus you will not need to recompile neither your application, nor domain.

BTW Adding new class is better, than modifying existing classes. You always can break something what was working before your modifications. That's why Single Responsibility Principle was introduced.

Another your questions:

  1. No, you are finding good examples of Decorator pattern usage (especially one in Head First book). Also take a look on IO Stream class and it's decorators for inspiration.
  2. Pattern just should solve problem. If you don't have problem, which pattern is targeting, than it's just wrong usage of pattern.
  3. Patterns do not stick to industry. They stick to problems of your code (often its about duplication of code)
  4. No, it's good pattern. Think again about Streams in .Net. Is it production code?
Sergey Berezovskiy
  • 232,247
  • 41
  • 429
  • 459
1

Generally in real world applications you will be dealing with more abstract objects (ie not things liek pizzas and coffees :))

A real world example of Decorator pattern is the Java BufferedReader class. It adds additional functionality to a FileReader for example.

The benefits are that you can change behaviour at runtime and you are not tied down to having many many different objects.

In your example, if I have four objects:

Pizza
Tomatoes
Cheese
Mushrooms

Then I can build a pizza with any combination of the four ingredients. Otherwise I would have to have a ton of classes to allow that behaviour e.g. PizzaWithTomatoesAndCheese, PizzaWithTomatoesAndMushrooms

cowls
  • 24,013
  • 8
  • 48
  • 78
  • @RomanYakimchuk Yes, the Head First is simply terrible all around. But examples of patterns elsewhere also tend to be contrived, infantilized and ultimately pointless (what does it mean to make a pizza anyway). Without a realistic example it's hard to tell if it should be a Builder or a Decorator or something else. – DPM Aug 23 '19 at 17:42
1

A "con" that I can think of about the Decorator pattern is that the design is rigid.

When you decorate an object, you typically don't know very much about the object because you are implementing a thin common interface. The interface could be fine for your algorithm when you first implement it, but change can be problematic.

Consider this example about a paint program. I love it because it illustrates two problems I see with the pattern.

One of the decorators is to fill the shape. How do you plan to fill a shape if the only thing you know about the shape is the common interface? At least if it was a program to paint only rectangles something not too ugly could be done keeping that same class design, but painting rectangles seems a contrived example...

Second is, how many things can you do when painting a shape? Basically just different types of border and different types of fill. Maybe shadow and text. The point is, if there are just one or two types of possible decorations, Decorator could be an over engineered solution.

An even more important problem, I think, is that every single wrapper plus the object executes its action (the implementation of the target method in the abstract Decorator class is delegate.operation()). So what happens when the requirements change and we shouldn't execute one after another anymore but rather one should supercede the others or the behaviour of a wrapper should depend on some value of the wrapee object.

As you say, I suspect the pattern is hardly ever used because you'd need stable requirements, several types of decorators to justify the overhead and true wrapping behavior where the value of the wrapee is always inconsequential.

In fact the only sensible examples that I could find are those in the Java core libraries, such as InputStream.

Another non-sensical example appears in the Head First book that you mention, where they use it to calculate the price of the coffee, where they create a litany of classes to calculate the price of a coffee (each class is exclusively used to add a new cost to the total!)

To me that's a sign that it's hard to find legitimate uses for this pattern in the wild.

DPM
  • 1,960
  • 3
  • 26
  • 49
0

I agree with the use of the Decorator pattern for one reason only:

  • SRP in a code-change management scenario. See the excellent example on page 72 of Working Effectively With Legacy Code. When you wish to compartmentalise your changes in a unit testing context (especially with brittle legacy code), Decorator is superb. It's great for separation of concerns, but like all such changes, it comes at a price (see below).

I disagree with the use of the Decorator pattern for several reasons, in the more general case:

  • It is potentially slow, especially if used in performance-critical sections of code. The repeat nesting of many decorators causes repeated JMPs in the generated assembly, which can have a severe performance impact.
  • It becomes like peeling the layers of an onion, when you have to read / parse a multiply-nested decorator, and figure out its order of execution (pre-order or post-order depth first traversal, in stack terms - or even a mix of both).
  • This idea of "wow, you can assemble any set of permutations you like using decorators!" is solvable in other ways, such as favouring composition over inheritance, where we exhaustively compose structs or classes in such a way that they can accommodate all possibilities of composition required for that class; e.g. a player character in a game may have a gun and / or a vehicle they are driving; so to know what these are, we provide fields for both (these may be either nested structs or pointers / references to other structs classes, allocated separately), whether used or not. This is common in entity-component systems for games.
  • A proliferation of classes is avoided, which may or may not be a good thing in your case.

Ultimately, composition is quite specialised and ignores SRP, but at the end of the day, most code is written for specialised purposes and not for endless re-use.

At the end of the day, I'd be happy to use Decorator during active dev / prototyping a feature, but aiming to switch it out for a more specific, hardcoded solution, later on as the featureset settles.

Engineer
  • 8,529
  • 7
  • 65
  • 105
  • By definition decorator is meant to `attach additional responsibilities to an object dynamically. Decorators provide a flexible alternative to subclassing for extending funtionality`. A decorator is about *polymorphism and dynamic composition*. You cannot substitute this pattern as you describe in your point 3 - that is a completely different solution. With a decorator you can compose a different object chain at runtime without having to hardcode anything up-front, and you would usually implement an interface and use it via composition – ironstone13 Jan 28 '20 at 19:40
  • @ironstone13 In terms of staticism / dynamism at that level, there's no difference: You still have to write classes before compiling, to enable new runtime-dynamic functionality. Permutations possible at runtime are even more dynamic with the gamedev solution, due to granularity. Only difference is that Decorator subclassing spreads things out more across memory and (code) cache; if you'd ever implemented basic OO in C/ASM (OOless), you'd understand this. Try to look up and understand how vtables and subclassing work under the hood (in native code). Like to learn more? Get in touch. – Engineer Jan 28 '20 at 21:14
  • I still think there is a difference from static / dynamic point of view: 1) you can use the classes from any assembly via plugin pattern, you are not limited to your code 2) composing of decorators can be dynamic, based on configuration, user input, whatever 3) number of combinations with dynamic composition is much greater than with static subclassing. Now regarding memory layout - this might be critical for gamedev, but for typical enterprise architectures this is negligible, there are always more impact-full bottlenecks. What do you mean `with the gamedev solution, due to granularity`? – ironstone13 Jan 29 '20 at 20:35