I just read an interesting blog post1 someone linked to on ruby-talk:
[Design p]atterns are signs of weakness in programming languages.
When we identify and document one, that should not be the end of the story. Rather, we should have the long-term goal of trying to understand how to improve the language so that the pattern becomes invisible or unnecessary.
The author examines in depth two examples: function calls (a "pattern" in assembly, trivial in higher-level languages) and object-orientation itself (a "pattern" in C, trivial in actually OO languages). He also suggests MVC as implemented in Rails and other agile Web frameworks may be a similar case.
I'm not sure about the MVC-in-Rails one, but his main examples are pretty compelling. Other people have written about how trivial it is to implement some of the canonical GoF patterns in dynamic languages like Ruby. How many of the GoF patterns are actually higher-level design constructs and how many are just language artifacts? Consider this Ruby module2:
module Flyweight def new(*args, &block) @flyweights__ ||= {} args = canonicalize(*args, &block) if respond_to? :canonicalize key = Marshal.dump(args).freeze return @flyweights__[key] ||= super(*args) end end
Just extend your class with it and your class is automatically an instance of
the Flyweight pattern. Magic! ergo, language artifact. I was originally going
to say that this doesn't work for the Decorator pattern, but then I realized it
does3:
module Decorator HANDLED = %w{ object_id __id__ __send__ singleton_method_added } def initialize(decoratee) @decoratee = decoratee @decoratee.methods.each do |mname| next if HANDLED.include? mname HANDLED << mname Decorator.module_eval <<-end_eval def #{mname}(*args, &block) @decoratee.__send__(:#{mname}, *args, &block) end end_eval end end end
Just include this module in your class to effectively sub-class instance
objects. Both non-defined methods and super() will invoke methods in the
@decoratee, just as you would expect.
With my first example "failing," I dug through my copy of the GoF book for a pattern I couldn't just implement in Ruby4. Maybe someone will point out that I'm wrong, but it looks like the Composite pattern is such a beast. Sans all the static typing hoops, it seems to boil down to "provide the same interface for all the nodes in a tree, whether or not they are leaves."
This really seems to emphasize the arguments about the design pattern concept's lack of formal foundation. What exactly is a design pattern? It appears that some of them are metaprogramming algorithms. Others are interface conventions. What about MVC? — it doesn't seem to slot easily into either of those two categories.
It might be interesting to go through the GoF book, see how many can be implemented as metaprogramming algorithms, and try to find common threads among the rest.
1 This article is also the fourth time this week I've noticed the blogosphere mention Garrett Rooney. When did you become blogofamous, Garrett?
2 Example code only, no warranty expressed or implied, etc etc.
3 It appears that method_missing doesn't get called for super, which is why
I resorted to doing it this way. Any better ideas?
4 "Implementing the pattern" vs. "implementing an instance of the pattern."
Commentary most sage
I got blogofamous? Crap, nobody told me!