Design Patterns Summary Notes

These are just some summarized notes I created as I read the Design Patterns: Elements of Reusable Object-Oriented Software book in order to anchor my personal experience with the vocabulary. If you have:

experience programming any kind of real-world project 

and/or 

hate seeing poor organization in your code and, thus, refactor your code even moderately often

and/or 

you've used Java past an introductory level (Java's idioms, structures, standard library, etc. incorporate pretty much every pattern),

then it is highly likely you'll already be aware of the motivation behind a sizable chunk of the patterns. However, it's never a bad idea to be familiar with common industry terminology in order to follow along and/or participate in design decision discussions.

Creational Patterns: how to avoid hard-coding instantiation of specific classes

- the following list proceeds in order of abstractness and flexibility

1. (Factory Method) define a base class and use that base class when requesting, creating, instantiating objects from a "creator class".  Then override/implement specific sub-classes for different behavior; e.g. when making a game, the "game coordinator" class can be concerned only with "characters, projectiles, obstacles" object types and perform all operations on those base types. Then sub-classes can implement specific types of characters (protagonist, enemy, ...), projectiles (rock, ball, ..), and obstacles (block, door, ...) without the game coordinator class having to be aware of those details.

2. (Abstract Factory) Like the Factory Method except more general or decoupled; pass in an object that contains the object instantiation or factory method functions to call. You can change the object to change what type of object returned when called.

3. (Builder) pass in a builder object that can either be triggered to "do its thing" or used piecemeal to build up a final output. Change the builder object to build different things. In practice the JSONReader and JSONWriter I've used with libGDX is an example of this, you can append or parse specific types of data using the methods according to a set protocol.

4. (Prototype) change the base object prototypes to build different things. If you've used JavaScript's prototypal inheritance then you know what this is.

5. (Singleton) ensure only a single instance of a class is used. Useful when you know if you only need one of some sort of "coordinator" or "repository" or program-wide media decoder.

Structural Patterns: this is split into structural class patterns (static) and structural object patterns (dynamic)

1. (Adapter or Wrapper) pretty much self-explanatory, you wrap an object with another object so its exposed interface is different but internally it calls the original object. The less flexible way is to do this "statically" with interfaces or inheritance. The more flexible way is to just pass in an instantiated object and call its methods.

2. (Bridge) separate abstraction from implementation. This is similar (if not exact) to the problem I was solving when working on my dynamic equation system. I had one hierarchy of equation values and then a separate display hierarchy that I needed to bridge the values to.

3. (Composite) used everywhere for nested or hierarchical scene graphs in all sorts of graphical applications.

4. (Decorator or Wrapper) another wrapper except unlike an Adapter wrapper a decorator is meant to mimic the interface of the object is it wrapping for pass-through calls. The decorator then adds its own functionality before or after the pass-through calls. Streams like in Java or Node are another good example.

5. (Facade) hides subsystem with interface, in turn the subsystems can also have facades themselves. The entire point is to decouple implementation details from users of the system and as such is just a specific and straightforward application of abstraction.

6. (Flyweights) are shareable objects; this is accomplished by removing externally dependent state from the object. Context is supplied by the embedding or calling environment of the flyweight object. In Java this idea is implemented using object pools; I also implemented a very similar idea with my dynamic equation system where an "equation value" could reside in multiple contexts.

7. (Proxy or Surrogate) self-explanatory, an object stands in as representation for another object and is responsible for negotiating with the source object

Behavioral Patterns: this is about managing "objects in use" and how they work together

1. (Chain of Responsibility) the most common implementation of this is an event system for a user interface. Events can "bubble up" to parent event handlers until it is handled. This would be an example of the Composite pattern combined with Chain of Responsibility pattern.

2. (Command) abstract "do something" into an object responsible for knowing what to "do something" to. A base "command" class exposes a trigger method and then sub-classes implement all the different types of commands possible. In an "undo/redo" system, the command object can store state necessary to enable that functionality. You can also build a subclass that executes or nests a series of commands, e.g. CommandSequence, which is an application of the Composite pattern to this behavior.

3. (Interpreter) in the most general sense, applying some transformation or operation over a class hierarchy is the interpreter pattern but it is specifically meant to refer to hierarchies for representing computer languages (e.g. an abstract syntax tree)

4. (Iterator) if you know what iteration is and have used an iterator object in code then you know this pattern already. It encapsulates the idea of "moving through an ordered data structure" into a separate class to increase organization and flexibility.

5. (Mediator) controls how a group of objects interact with each other. Each object only needs to know about the mediator object instead of all related objects it needs to interact with. A good example from experience and also mentioned in the book is that of a dialog box where a widget's behavior can depend on the state of other widgets (e.g. a button becomes enabled after some text is entered into a text field)

6. (Memento) a source object saves its state into another state-holding object and that state-holding object is stored in some place until the source object or program requests it (e.g. comparing current and previous state, undo/redo, etc.). Only the source object can store and retrieve information from the state-holding object. This allows a source object's state to remain private while being preserved.

7. (Observer) one of the most, if not the most, familiar example of this is the model-view-controller (MVC) organization for user interfaces. The underlying data model is the subject and the different visual (or other) representations are the observers. This relationship can vary between fully one-way (data model updates the visuals) and fully two-way (users interact with visuals and thus update the model and the model updates the visuals) depending on the design constraints.

8. (State) if one is using enums to set and track an object's state and the conditional code created to handle the different states is very involved then using a state object can be a solution. The state object encapsulates all the associated logic and handling of a particular state. I used this idea (and it is also an example mentioned in the book) when creating different tools for my graphics editor; activating a tool would put the editor into a specific state.

9. (Strategy) this is basically the class-version of the motivation behind creating the concept of functions or modular blocks of code in imperative programming; you have a set of algorithms meant to accomplish some task (e.g. flood-fill for some spatial partition data structure) and would like to swap them out or use them in conjunction. Strategy encapsulates these algorithms into their own subclasses to decouple them from the calling class.

10. (Template) write an algorithm using non-implemented function stubs in order to describe a specific sequence of steps; class implementations are responsible for filling in the details. This is similar to the Strategy pattern but applied for an entire algorithm. This is great for top-down coding where you define the "shape" of how your program works but allows variation in implementation. It's also a similar style of thinking as Test Driven Development (TDD). The example in the book is defining the set steps of opening a general document and leaving the opening details to specific subclass of document such as DrawDocument, WordDocument, etc.

11. (Visitor) This is the only pattern I was completely unfamiliar with and, perhaps not coincidentally, my least favorite pattern. You abstract an algorithm or operation into a set of objects which parallels whichever structure you want to analyze, transform, etc. As you walk the structure, you pass an "operation node" to each node which, in turn, calls the matching operation in the "operation node". Thus, the "operation/visitor node" must have separate functions for each type of node it visits. You can see how this can quickly become brittle if you need to extend the object structure in any way so the pattern works for a relatively static structure but one wants to add further possible operations on that structure.