designates my notes. / designates important.
Overall this was a great book. I will keep it in mind as the go-to resource if I ever need a refresher on what pattern I might want to use in a situation where the answer is not obvious.
I really like the layout, with each section offering motivation, theory, structure, example code (in C++ and Smalltalk), and related patterns.
In hindsight I can say that the authors succeeded in their goal of introducing a common language so that developers could communicate at a higher level. 25+ years after its publication it is still a valuable resource and we still use the names the authors assigned to the various patterns.
Chapter one introduces the book itself. What is it? A catalog of design patterns.
Experience shows us that we do a lot of the same things, solving similar problems, again and again. This book tries to put down what those similar solutions look like, describe them, and name them.
The four essential parts of a pattern are its name, what problem it addresses, the solution it offers, and what consequences it has.
There are three main types of patterns, creational, structural, and behavioral. The book is divided into chapters covering each of these high level types.
The old stand-by is reiterated.
Program to an interface not an implementation.
They also see, quite early, that composition is at least as good, and today I think most would agree better, than inheritance.
The authors offer some advice on how to use the catalog, how to choose patterns, and how to go about implementing a pattern. Like some other books I have read, the make a point that name selection based on the context you are working is is absolutely key. In my opinion naming, of functions and variables, is probably the most important aspect of software design.
Chapter two is a case study in the design of a simple document editor that includes such things as glyphs, analysis (spell checking and formatting), layout, and the UI. This is used throughout the book to help motivate several of the patterns. Here it is used to introduce 8 patterns: composition, decorators, strategy, abstract factory, bridge, command, iterator, and visitor.
Chapters three, four, and five are the design pattern catalog.
Chapter three covers the creational patterns, including abstract factories, builders, prototypes, and singletons.
Chapter four covers the structural patterns, including composite, adapter (wrapper), proxy, flyweight, facade, bridge, and decorator.
Most of these patterns are similar/related, but apply to different problems.
For example, the adapter and bridge are almost the same, but used at different points in development. The adapter is used later in development to stick together two things that weren’t foreseen to go together while the bridge sticks them together from the onset, understanding that they will be evolved independently but must work together.
Chapter five covers the behavioral patterns, including chain of responsibility (event handler), command, memento, observer (publish-subscribe), mediator, iterator, interpreter, strategy, template, and visitor.
The state pattern is presented with an example of TCP connections and drawing programs where each tool is a state.
Overall I think this is a must-read for any software developer. Even with its age it is still as relevant today as it has ever been.
On the other hand, I see more functional programming on the horizon, given multi-core processors being the trend that will be followed for the foreseeable future (since we’ve about reached the limit in terms of speed and size for a single core). Still, I think the ideas here are as useful to a functional designer, even if they won’t be implemented in the same way.
Class patterns deal with relationships between classes and their subclasses. These relationships are established through inheritance, so they are static—fixed at compile-time. Object patterns deal with object relationships, which can be changed at run-time and are more dynamic. Almost all patterns use inheritance to some extent. So the only patterns labeled “class patterns” are those that focus on class relationships. Note that most patterns are in the Object scope.
Creational class patterns defer some part of object creation to subclasses, while Creational object patterns defer it to another object. The Structural class patterns use inheritance to compose classes, while the Structural object patterns describe ways to assemble objects. The Behavioral class patterns use inheritance to describe algorithms and flow of control, whereas the Behavioral object patterns describe how a group of objects cooperate to perform a task that no single object can carry out alone.
Program to an interface not an implementation.
I wonder if this is where this comes from or if they are simply restating it.
Composition is discussed as an alternative to inheritance.
it’s often said that “inheritance breaks encapsulation” [Sny86]. The implementation of a subclass becomes so bound up with the implementation of its parent class that any change in the parent’s implementation will force the subclass to change.
The Singleton pattern is an improvement over global variables. It avoids polluting the name space with global variables that store sole instances.
It seems to me nothing more than a fancy way to achieve global state.
Adapter focuses on resolving incompatibilities between two existing interfaces. It doesn’t focus on how those interfaces are implemented,nor does it consider how they might evolve independently. It’s a way of making two independently designed classes work together without reimplementing one or the other. Bridge, on the other hand, bridges an abstraction and its (potentially numerous) implementations. It provides a stable interface to clients even as it lets you vary the classes that implement it. It also accommodates new implementations as the system evolves.
As a result of these differences, Adapter and Bridge are often used at different points in the software life cycle. An adapter often becomes necessary when you discover that two incompatible classes should work together, generally to avoid replicating code. The coupling is unforeseen. In contrast, the user of a bridge understands up-front that an abstraction must have several implementations, and both may evolve independently. The Adapter pattern makes things work after they’re designed; Bridge makes them work before they are.