So many pages on the internet attempt to explain a concept and then do so with an example that demonstrates a totally inappropriate use of the concept. I'd go so far as to say that the example is so bad that it no longer demonstrates the concept at all.
The taco example is an example of an OOP antipattern. Its classic "naive OOP", i.e. "I can do this, so I should". If the taco where in an order taking system, a "menu item" object might have an associated list of "condiment" objects, and these would probably be associations to shared objects. If this were a video game, there might be separate behavioral and rendering objects, and the "rendering object" would simply be a number of generic models etc etc. I understand its hard to provide good examples when writing a blog, but this is precisely the situation when one should do so. To describe the decorator pattern and then provide examples that are bad to the point of misinformation is just lazy.
The decorator pattern is already on sketchy ground IMHO. The wikipedia entry[1] says its to avoid separation of concerns issue, but it seems to do so by ignoring the IsA constraint. A WindowDecorator IsNotA Window. A DecoratedWindow might be a Window, and it might have a list of Decorators, but I can see all sorts of ways that a specific implementation will have a radically different class hierarchy.
Agreed. I hope nobody learns this pattern and looks for a project to inject it into. Design patterns should generally be avoided unless there's an obvious need for them where they simplify your project. In my experience, it is incredibly rare. For the benefit of mankind, this is something that I think needs to be made clear with every discussion about design patterns.
Each well-known pattern has its own applicability and use cases, which have been analyzed and described very well. I wonder, what makes you think, that all of it should be dismissed?
Go learn Clojure or any LISP language and solve some problems using it. Watch the videos by Neal Ford and Rich Hickey. Think about your data. Then come back and reflect on it.
>Each well-known pattern has its own applicability and use cases, which have been analyzed and described very well. I wonder, what makes you think, that all of it should be dismissed?
The GoF Design Patterns appeared after the GoF observed that these patterns happened in some C++ code, and thus they decided to formalize them and document them.
All fine.
The question is why did these patterns appear? Because programmers need to overcome the shortcomings of whatever programming language they are using.
Other languages don't have the shortcomings of C++ or Java, thus, many of the GoF patterns simply don't apply or aren't needed.
Another topic is that the patterns force the person to behave as a compiler; that is, to manually create abstractions that should be supported by the language in the first place.
Languages with metaprogramming, like the Lisp family of languages, avoid the burden of having to do this. You can just program the abstraction as a macro once, and then apply it repeatedly whenever you want, without having to write more lines of code.
> Languages with metaprogramming, like the Lisp family of languages, avoid the burden of having to do this
Using macros or higher order functions doesn’t make the concept of a pattern go away. In Lisp you also use adapters, factories, strategies and the like. They’re just simply functions rather than overly complicated objects.
Since its only functions on data structures, chances on reuse are indeed much larger.
>They’re just simply functions rather than overly complicated objects.
And thus the pattern becomes almost invisible. Instead of having to fully apply it the java (or C++) way, that is, for example creating a separate class for each Strategy, etc.
Exactly. My point was more that the OO patterns refer to general concepts, that are useful to be able to recognize without objects cluttering your view.
Software design pattern is a mental and communication concept, which has nothing to do with compilers/languages/etc. Design phase happens before you start writing your code - you have to first think about how the system will work and what will be its internal structure, and that's the moment when patterns start working. Then, even in Java you don't have to call your class "SomethingStrategy": you may simply follow the pattern to correctly implement what happens to be a strategy or to explain the behavior of the component to your colleagues. When you have the common language based on pattern names, it's much easier to say: "Look, this is a Builder for data model, which offers convenient internal DSL", rather than explaining everything from scratch. Patterns are ubiquitous tool for developers, not an evil. The evil is incompetence, with which some people blindly apply various programming concepts in totally inappropriate ways (including design patterns, functional and parallel programming etc).
Serious question: do you consider all the patterns in the gang of four to be terrible or are there a few you'd recommend to be more useful? As a relatively young Java developer I'd love to know what patterns have stood the test of time.
In essense a decorator /is/ a function. Objects (OO) make this more complicated by wrapping objects inside objects.
In functional programming you still use concepts like decorators, adapters, strategy. But they’re more natural to use and don’t require lots of explanation like with objects.
In modern Java your decorators can simply be a Function type.
I can't speak for the original commenter, but I think that book has (unintentionally) done a lot of damage to software by getting people excited about things they have no business getting excited about. It's impossible to read that book as a junior developer and get any use out of it. What inevitably happens is the junior developer reads it, gets excited about the patterns, and injects as many patterns as they can into all of the source code they write. I've done it myself, and I've seen this happen to others time and time again.
You can accomplish writing perfectly clean, readable, and maintainable projects that multiple people work on without learning any of the design patterns in that book. You will probably discover later that you were using certain patterns without knowing what they were called. The earlier you learn this, the better off you will be.
> You can accomplish writing perfectly clean, readable, and maintainable projects that multiple people work on without learning any of the design patterns in that book. You will probably discover later that you were using certain patterns without knowing what they were called. The earlier you learn this, the better off you will be.
The main value of learning design patterns is not how or when you use them, but in the way they establish a common language that people can use to describe somewhat complex designs and/or the intent behing a design in rather simple terms. Design patterns facilitate creating conceptual models of software constructs, thus they help explore ideas and communicate them with others while focusing on essential aspects and ignoring implementation details.
For instance, at first glance the strategy pattern may be seen as a fancy word to describe inheritance, but mentioning inheritance says nothing about the intent of implementing algorithms as strategies. If, on the other hand, someone says that a component will be implemented based on a strategy pattern then it's already implicit that the designer expects that an algorithm should be exchangeable with ease and also that more versions could be added later without any trouble.
I think i must cite hellofunk's [HN user] comment on Design Patterns, perhaps the best comment i've read regarding this topic:
"Lisp in general is about exploring a particular problem using only the constraints of that problem, without concern for how different problems get solved. By contrast, design patterns are about forcing a common solution onto a range of different problems."
17 comments
[ 0.23 ms ] story [ 22.6 ms ] threadThe taco example is an example of an OOP antipattern. Its classic "naive OOP", i.e. "I can do this, so I should". If the taco where in an order taking system, a "menu item" object might have an associated list of "condiment" objects, and these would probably be associations to shared objects. If this were a video game, there might be separate behavioral and rendering objects, and the "rendering object" would simply be a number of generic models etc etc. I understand its hard to provide good examples when writing a blog, but this is precisely the situation when one should do so. To describe the decorator pattern and then provide examples that are bad to the point of misinformation is just lazy.
The decorator pattern is already on sketchy ground IMHO. The wikipedia entry[1] says its to avoid separation of concerns issue, but it seems to do so by ignoring the IsA constraint. A WindowDecorator IsNotA Window. A DecoratedWindow might be a Window, and it might have a list of Decorators, but I can see all sorts of ways that a specific implementation will have a radically different class hierarchy.
[1] https://en.wikipedia.org/wiki/Decorator_pattern
Each well-known pattern has its own applicability and use cases, which have been analyzed and described very well. I wonder, what makes you think, that all of it should be dismissed?
The GoF Design Patterns appeared after the GoF observed that these patterns happened in some C++ code, and thus they decided to formalize them and document them.
All fine.
The question is why did these patterns appear? Because programmers need to overcome the shortcomings of whatever programming language they are using.
Other languages don't have the shortcomings of C++ or Java, thus, many of the GoF patterns simply don't apply or aren't needed.
Another topic is that the patterns force the person to behave as a compiler; that is, to manually create abstractions that should be supported by the language in the first place.
Languages with metaprogramming, like the Lisp family of languages, avoid the burden of having to do this. You can just program the abstraction as a macro once, and then apply it repeatedly whenever you want, without having to write more lines of code.
Using macros or higher order functions doesn’t make the concept of a pattern go away. In Lisp you also use adapters, factories, strategies and the like. They’re just simply functions rather than overly complicated objects.
Since its only functions on data structures, chances on reuse are indeed much larger.
And thus the pattern becomes almost invisible. Instead of having to fully apply it the java (or C++) way, that is, for example creating a separate class for each Strategy, etc.
I believe we're on the same page :)
In functional programming you still use concepts like decorators, adapters, strategy. But they’re more natural to use and don’t require lots of explanation like with objects.
In modern Java your decorators can simply be a Function type.
You can accomplish writing perfectly clean, readable, and maintainable projects that multiple people work on without learning any of the design patterns in that book. You will probably discover later that you were using certain patterns without knowing what they were called. The earlier you learn this, the better off you will be.
The main value of learning design patterns is not how or when you use them, but in the way they establish a common language that people can use to describe somewhat complex designs and/or the intent behing a design in rather simple terms. Design patterns facilitate creating conceptual models of software constructs, thus they help explore ideas and communicate them with others while focusing on essential aspects and ignoring implementation details.
For instance, at first glance the strategy pattern may be seen as a fancy word to describe inheritance, but mentioning inheritance says nothing about the intent of implementing algorithms as strategies. If, on the other hand, someone says that a component will be implemented based on a strategy pattern then it's already implicit that the designer expects that an algorithm should be exchangeable with ease and also that more versions could be added later without any trouble.
I think Command and Iterator have merit.
Visitor is useful if you are in Java since you don't have union types or pattern matching.
"Lisp in general is about exploring a particular problem using only the constraints of that problem, without concern for how different problems get solved. By contrast, design patterns are about forcing a common solution onto a range of different problems."