I co-author developmentor's Code smarter with Design Patterns in .NET course with Kevin Jones, and ever since the creation of the course I've been on the look out for a good book to recommend to students. The one I currently recommend is the excellent Head First Design Patterns, although the only rub is its in Java, but in terms of teaching patterns its awesome.
I recently decided to order the latest Design Patterns for C# v3.0 from OReilly written by Judith Bishop, and see if I could recommend that text. Yesterday it arrived, and I skimmed though it looking at the various classic patterns Observer, Template..and my was I shocked...the implementation of some of the patterns is just plane weird, and some of the patterns basic structure is plainly wrong as defined by GOF.
I've not been through the entire catalogue of patterns, but here are some examples.
Template pattern is shown to use composition as opposed to inheritance to bind the template method to the template steps, this is plainly wrong and the implementation is therefore a strategy pattern. As for the strategy pattern examples none of them really effectively show the use of supplying runtime specific behaviour to a subsystem or algorithm.
The observer pattern uses a weird combination of delegates and IObserver interface, its either one or the other, and for .NET is almost certainly pure delegate/event. The is no obvious reason why she has used both interface and delegates/events
The Abstract Factory pattern makes use of a generic interface for the Abstract Factory, but no where in the interface is the generic argument references, and thus is seemingly redundant.
The command pattern mixes the implementation of the invoker with the implementation of the command.
In conclusion I'm not convinced the author either knows the patterns as defined by the GOF, nor is highly proficient in C# v3.0. So my quest continues to find a good C# patterns book I can recommend for class..
The essence of the template method pattern is a method where some parts of the algorithm are provided, and the rest are provided by additional methods that need to be plugged in. The traditional GoF implementation would indeed put the template method in an abstract class, and then various subclasses would provide implementations of the "plugged in" methods.
However, the GoF book also gives us the admonition to prefer composition over inheritance. I personally find the implementation in the C# 3.0 design patterns book (example 7-5, p. 160) to be quite elegant. It preserves the essence of the template method, which provides a computation where two aspects are provided by other methods. Instead of being in the same class as the template method, however, they are provided by a passed-in reference to an instance of the "IPrimitives" interface. Various implementations of IPrimitives then fill in the details of the sub-parts of the algorithm, in much the same way subclasses of an abstract template method class would.
It is true this implementation of template method has elements that are similar to the Strategy pattern. However, a long-known critique of the GoF design patterns is that it is not possible to write a program that can unambiguously detect design patterns in existing code. That is, the mapping of code to a design pattern is often one of overlaying a specific interpretation on the underlying code.
As for the Observer implementation, this seems fairly straightforward to me. The IObserver interface is designed to permit the observer capability to be mixed-in to almost any class. The use of events to call the Update method of a class implementing IObserver also makes sense for C#.
While it's true that the typing of the Callback delegate makes it such that any method matching the signature can be used as the Update method, it still is beneficial to have the IObserver interface as a way of communicating to readers of the code that the class is being used in an observer role. In the absence of this IObserver interface, a reader of the code would have to be very alert to see that the "subject.Notify += Update" at the end of the constructor means the class is acting as an observer.
The comment about the Abstract Factory example is puzzling. The generic factory interface is used at line 81, page 128. The "brand" falls through from the new call in Main() (lines 94-96, also p. 128).
What is nice about the use of generics in the Abstract Factory example is they eliminate the multiple implementations of the various sub-factories and sub-products. A traditional implementation of Abstract Factory provides trivial implementations of Factory1, Factory2, ProductA1, ProductA2, ProductB1, and ProductB2 (6 subclass implementations). In the book's implementation, there is a generic Factory<Brand>, a generic Bag<Brand> (product1), and a generic Shoes<Brand> (product2) for a total of 3 implementations. The implementation savings of this approach get bigger as more products are added. This is a very elegant implementation of Abstract Factory, and one that fully leverages the features of C# 3.0.
I have carefully read and studied the C# 3.0 design patterns book by Judy Bishop, and have found that its biggest strength are the elegant pattern implementations that preserve the essence of each pattern, but use C# 3.0 features in effective and innovative ways. Since you already have the book, I heartily recommend that you give it a second chance, and a careful read. The world was not frozen in place with the original GoF book; innovation is still possible in design patterns, as this book shows.
The implementation provided for the template pattern is the strategy pattern, in all but name, and here lies the problem. Patterns offer the re-use of solutions which is great but there main benefit is in communication, the word template pattern means something to an engineer, in terms of structure and flexibility. Too simply hijack the name of the pattern for another implementation greatly dilutes the power of patterns as a communication tool.
As for the observer pattern, you only need to implement it with events, there is no need to provide your own Attach/Detach logic. .NET events is 90% of what you need to implement the observer pattern. The implementation is a mix of both approaches, either one is fine but not a mix of both. Either use events or your own registration.
As for the abstract factory, again this is an implementation that assumes that the items your factory wishes to create has a public parameter less constructor ( imposed by the generic new constraint ). I can’t think of many cases where I’ve used a factory that creates types with a parameter less constructor. Personally I think an example that shows a more flexible creation method would be far more interesting and useful.
As for my comment, page 126, lines 7 to 10
Where Brand : IBrand
I’m at a loss for the use of the generic type argument and the constraint since the type argument Brand is never used.
As for your comment that this book makes good use of C# features I would have to politely disagree whilst there is numerous C# features being used in many cases they are not being used effectively. For me observer pattern should be using events, strategy pattern which only contains a single strategy is best implemented with a delegate.
I agree patterns are not set in stone and evolve, and I’ve certainly done this myself but to refer to them with the name given by the GOF and change the structure, and forces for the pattern is clearly wrong. The use of patterns as a communication medium is in my mind one of their greatest strengths, scaling a development team requires efficient accurate communication. If there are new patterns/techniques they really should have their own name. For example I often show students various Singleton implementations, a single instance for the life of the app domain, a single instance per thread, only a single instance of the object at any point in time. All of these are singleton like patterns but I give them different names.
Finally if the book wishes to re-interpret the pattern and produce a different structure it should make that clear. I often show students the straight forward GOF approach first and then evolve more efficient .NET implementations that way they understand the roots of the pattern and can converse with developers on a wide range of platforms.
Oh forgot my last point on the IFactory<Brand>
Further the use of the type argument breaks the pattern, since the client now needs to have additional knowledge of the type of components the factory is building since the client must hold on to a reference of a specialised IFactory<Brand> as opposed to simply IFactory.
The client can't hold a reference in terms of IFactory<IBrand> since .NET generics does not support variance.Removing the use of the type argument on IFactory would resolve this. Since there is no need for the interface to have the type argument.
I haven't read it, but supposedly Design Patterns by Christopher G. Lasater is the best one.
The guys at Data & Object Factory have created .NET projects for all Head First Design Patterns examples.
See 'Bonus 2' on this page:
Work have purchased a licence but I haven't had a chance to look at them yet
Post a Comment