I recently finished classic “Design Patterns: Elements of Reusable Object-Oriented Software”.
It was highly informative and even playful to bounce thoughts in abstract world of interfaces.
The book is very eloquent, clear, and friendly.
I will not attempt to explain patterns again, nor show why they are needed. If you want to learn them, just go read the book!
Instead, here is a collection of interesting anecdotal extracts.
About the book
- published in 1994, but collection of patterns dates back to 1970s
- one foreword is from G.Booch, Chief Scientist, Rational. You’ve got to be hardcore scientist to study OOP.
Interesting glossary and terms
- coupling - degree on which software depends on each other.
- abstract coupling - class A maintains reference to abstract class B, A is abstractly coupled to B. It is coupling by types, but not concretes instances.
- black-box reuse - composition. Composed objects reveal no internal details, and thus black boxes.
- white-box reuse - inheritance. Object may have access to internal details of its parent.
- concrete class - class with no abstract functions. It can be instantiated.
- dynamic binding - runtime association of request to an object. In C++ virtual functions. Things that make abstract class abstract.
- encapsulation - result of hiding a representation and implementation of an object.
- framework - a set of cooperating classes that make a reusable design for specific class of software. Developer has to inherit, or compose classes from framework.
- polymorphism - the ability to substitute objects of matching interface at runtime.
- recursive composition
- embellishment - object that adds responsibilities to objects it wraps (e.g. shades, border, scroll bar)
- adoptee - something that adapter helps to adapt for external callers to use
- implicit receiver - request is made, but client has no explicit knowledge who is going to handle it
- single dispatch - method is resolved from its name and who (type) is calling it
- double dispatch - method is resolved from its name and who (type) is calling it and what is arguments
- multiple dispatch - method is resolved by some many types
- Class Inheritance = Interface Inheritance + Implementation Inheritance
- Interfaces are fundamental to OOP.
- Inheritance breaks encapsulation. This is because parents, often at least in part, define private details of their children.
- Program in interfaces, not implementations.
- Composition != Inheritance. Composition is better than Inheritance. (e.g. React was so successful because of composition)
- Each pattern is a structure with multiple elements, interfaces of their communication, that solves specific goal. The goal typically move out something that varies.
why? is the most important piece of pattern. it is more important than
- transparent enclosure = combination of: 1) single child, single parent composition; 2) fully compatible interfaces. Clients can’t tell if it is component or its enclosure.
Some piece of construction varies
- Abstract Factory - construct families of objects (e.g. Windows windows, MacOS windows)
- Builder - construct single complex object
- Factory Method - subclasses decide how to implement single function, function is called in parent class internals
- Prototype - create instance first, and then copy it and modify when you need it. Prototype manager constructor called with concrete implementations, has abstract ones internally.
- Singleton - make single instance without shared global variable, rely on static class and private-public mechanisms
Some piece of structural relations between objects varies
These four are enclosures of single object
- Adapter - transform interface of adoptee into form acceptable by external clients
- Bridge - different implementations of same interface. You can do single abstract class and inheritance, but inheritance would binds interface to implementation forever. Bridge breaks it into
..Implementation interface, so you have interface (Window), Implementation Interface (WindowImplementation), Concrete Implementation (MacOSWindowImplementation).
- Decorator - transparent enclosure, adds responsibilities to object dynamically. This is skin of object (as opposed to Strategy that is body). One decorator wraps other decorator wraps other decorator.
- Proxy - placeholder for some other object, has same interface. Can be resolved to original object later. (e.g. image that is being downloaded)
These structure multiple objects
- Composite - recursive structure, object that contains children has same interface as children
- Facade - single point of exit for system with many pieces. Make external clients to route requests through single point to all internally interviened systems.
- Flyweight - reuse objects transparently, allow efficient usage of resources. Important to differentiate intrinsic (stored in flyweight) and extrinsic state (dictated by context of where flyweight is called).
Some piece of behavior varies.
- Chain of responsibility - pass request from one handler to other. All handlers
Handle with same argument. Handlers can pass handler without action. Chain of handlers need to be constructed by someone. Handlers can keep next handler inside of them. Only single handler is outgoing from each handler. No guarantee that any handler will run request.
- Command - single function call as an object. This is how
functional closure could be implemented in C++. Binds arguments and who is going to execute it.
- Interpreter - duh, interpret language
- Iterator - traverse items one-by-one in different styles.
implicit iterator = increments itself, you run it like
map(lambda x: x ** 2, [1, 2, 3]).
explicit iterator = need to be incremented, you run it as a for loop
for x := list.Start(); ! x.Done(); x.Next().
- Mediator - when multiple objects talk to each other, to avoid
O(N^2) complexity of iteration between
N components, use single node to coordinate their interactions.
- Memento - extract private state from object. Make object possible to restore itself from private state. Note, client that extracts private state does not need to know what is inside. (e.g. binary of OS. I don’t know what is inside, but OS can run it and restore application into its expected state. Binary is memento).
- Observer - publish-subscribe model, but with objects
- State - same entity, all states share common interface, but their logic differs in each state, can transition from one state to other. (e.g. TCP Connection).
- Strategy - encapsulate whole algorithm into object. You can do this in Go. Can call specific methods of algorithm.
- Template Method - child classes define how method has to be implemented. Parent class uses method. (Note how similar to Factory Method!).
- Visitor - make abstract visitor (visitor-A, visitor-B) that can be visited by any visitor, call each visitor function inside. Make abstract visitor (visitor-X, visitor-Y) that can visit any visitor, define function of visit for each item. Can add operations on visitors without need to change them much. You can use it with iterator. I too come up with this when working on my project, so it is very natural.
why? is very important for interfaces, patterns, OOP, and life