다음을 통해 공유


Design Patterns Are Not Outdated

A comment left on my answer to a question over on Stack Overflow has me a little worked up.  I've seen this meme come out of programmers more and more and it just doesn't seem accurate.  The statement goes something like this, "Design Patterns were only useful because C++ (or Java) was so broken."  The implication is thus that design patterns belong in the dustbin of history now that we have moved on to more enlightened languages like Python or Ruby.  In this particular instance the commentor was talking about the strategy pattern.  His (?) assertion was that the need for the strategy pattern is not present in a language with first class functions.  My response is three-fold.  First off, this argument is historically ignorant.  The design patterns came as much from Smalltalk as from C++.  Second, it is not strictly true.  First class functions alone don't obviate the need for the strategy pattern.  Finally, providing an alternative implementation does not make the first implementation bad.

The argument that design patterns generally are due to flaws in C++/Java are historically innaccurate.  Design patterns (like much of modern programming) originated in the Smalltalk community.  Smalltalk is dynamic.  It has first-class functions.  Most of the flaws pointed to in C++ which "modern" languages solve did not exist in Smalltalk.  The patterns, then, have value independent of that flaws and the accused language.  For instance, the Strategy pattern is covered not just in the Gang of Four (where it happens to have a C++ example), but also in the Design Patterns Smalltalk Companion which points out that it is used in Smalltalk in the MVC framework at the heart of Smalltalk's GUI.  The controller is a strategy pattern.  It is also used ImageRenderer and a few other examples.

First class functions do not, by themselves, obviate the need for a strategy pattern.  This is more a nit than a real argument, but it remains true nonetheless.  To say a language has first class functions means merely that functions can be passed as data types.  It does not necessitate that the language implements closures.  Closures are a way of capturing the context of the function.  They can be used to retain state between calls to a particular function instance.  Without closures, there is no state.  Without state, many of the strategy pattern uses fail.  Consider for a moment using a strategy pattern to encapsulate encryption algorithms.  Without closures (or objects), you would have to pass the encryption key to the function every time it was used.  This is possible, but not terribly elegant.

The existence of an alternate implementation does not make the original implementation any less useful.  The fact that I can get much of the power of OO out of first class functions and closures does not mean OO is now of no value.  There are advantages to both techniques.  Empirically there does appear to be power in OO that is not captured (easily) by purely functional languages.  Most successful functional (or psuedo-functional) languages have adopted OO features eventually.  See Python, Ruby, Common Lisp, Scala, etc.  All added or have object-oriented features.  Let us return to the example of the strategy pattern.  Is its utility obviated by the use of first class functions plus closures?  In many cases it is.  Certainly it could be in the encryption example.  On the other hand, strategies are often more complex than mere functions can express.  The controller in MVC is a strategy.  In anything but a toy application, the controller will consist of multiple functions.  Sure, one could create these functions with the same closure and thus share state, but is that really a superior model?  I would argue that it is not.  In this case it would seem a less clear mechanism because the fact that the functions are tied together is less discoverable.  OO languages and functional languages each do things differently.  Things that are easy in one are more difficult in the other.  Neither is superior in all respects.

It should be noted that when I say "design patterns" above I am referring to it in the common sense of the object-oriented programming design patterns made popular by the Gang of Four book.  In a more general sense, each language has its own set of patterns and these can also be thought of as design patterns.  Some of the OO patterns are specific to OO languages and the need doesn't translate to functional languages.  Others are more "in the large" and likely translate well to all languages trying to solve big problems.  It is my claim, however, that most of the GoF patterns will be useful in any OO language.  They are not artifacts of particular implementations such as C++ or Java.