Head First Design Patterns - Summary
From D-Wiki
[Be aware... This is version 1. Class diagrams and other infographics to illustrate everything will follow...]
Chapter 1 - Welcome to Design Patterns
- Inheritance doesn't solve all problems because in some cases you don't always need a specific method. Or you even need a special method that other inherited methods don't need.
- You can then encapsulate the methods which are different and thus apply the specific methods where you actually need them. (Not on all elements...)
- You can use special interfaces for each functionality you want to add. (If you need it you can also use abstract classes for this...)
- With this you get a lot of small interfaces which encapsulate different functionalities. Then you can reference those with instance objects and use the needed functions like that.
- You can even setup different functionalities inside those classes to change the behaviour of those "properties" on runtime.
- Composition ("Has-a"-relations) are better than interitance ("Is-a"-relations), especially because of encapsulation and runtime behaviour changing.
-> That's basically the *Strategy Pattern*
- With those Design Patterns you can easily explain more complex architectural "structures" in a simple and short way. So keep them in mind. ๐
Chapter 2 - Keeping your Objects in the know
- We've got an object which handles subscriptions and when an update happens it gives this information to the subscribed object which do the changes. (Observer)
- With this way you can super easily make changes on different objects on an event firing, on runtime without changing the program code.
- "The Observer Pattern defines a one-to-many dependency between objects so that when one object changes state, all of its dependents are notified and updated automatically."
- It's super neat because you don't fixed dependencies between notifiers and the classes which do the updates. (Loose coupling)
- "Strive for loosely coupled designs between objects that interact."
- Java btw has an build in observer, but you got disadvantages like using a "direct class". (I think it's even outdated right now...๐ )
Chapter 3 - The Decorator Pattern
- Open-Close Principle: Classes should be open for extension but closed for modification.
- You can achieve that easily with decorators.
- We can "attach" different objects to a main class to change different functionalities of the main class accordingly.
- Decorator got the same type as the supertype which they decorate. Thus they add a new "property" either before or after that to the main class and then do the rest of the job.
- "The Decorator Pattern attaches additional responsibilities to an object dynamically. Decorators provide a flexible alternative to to subclassing for extending functionality."
- You can then call the Decorator with the base object to get the additional info.
Chapter 4 - Baking with OO Goodness
- Object creation based on parameters should always be encapsulated.
- You can do this really well with "Factories" which always give out the correct object with the given parameters.
- A factory is basically an object which (under certain circumstances) gives out fitting object instances of a certain type.
- This lets subclasses do the real decisions via polymorphism and the corresponding, called object.
- "The Factory Method Pattern defines an interface for creating an object, but lets subclasses decide which class to instanciate. Factory Methods let a class defer instanciation to a subclass."
- Dependency Inversion Principle: Depend upon abstractions (abstract classes and/or interfaces) and not concrete classes.
- This way dependencies get "inverted" and the "complete" objects are then dependent of the abstractions and not the other way.
Some guidelines for that...
- No variable should hold a reference to a concrete class.
- No class should derive from a concrete class.
- No method should override an implemented method of any of its base classes.
But those are just some guidelines. No pressure. ๐
You can and should not always follow these. But you should be following them often.
- A factory gives out an interface for the creation of a family of products. (So this is all decoupled...)
- You can even do this in a nested way to never really define it in a specific class, more detailed.
- "An Abstract Factory Pattern provides an interface for creating families of related or dependent objects without specifying their concrete classes"
- Often abstract factories are implemented as normal factories... ๐
Chapter 5 - One of a Kind Objects
- A singleton is an object which always gives out the only unique instance of an object.
- It ensures that a class only has one instance and provides a global point of access to it.
- When using multithreading you have to use the "synchronized" keyword. Even though it's expensive to use.
Chapter 6 - Encapsulating Invocation
- With a command pattern you can decouple different actions into commands, which can be then executed by a requester/client.
- We've got a basic client which is reponsible for creating command objects.
- The command object consists of a set of actions on a reciever. The command object then encapsulates an action by providing an execution method which can be invoked. (It knows how to perform a request...) The command object then gets put in an invoker object via a setter. The Invoker object is responsible for invoking/executing the command object by calling the corresponding execution method which then results in the reciever having invoked the different actions.
- The command pattern encapsulates a request as an object, thereby letting you parametrize other objects with different requests, queue or log requests and support undoable operations.
- A "NoCommand" object is kinda like a "null object". But it's at least besser if you don't have anything meaningful to return.
- With this pattern you can also implement "undo" functions super easily by providing an "undo"-function in your command and then doing the exact opposite as your "execute" function.
- Just as an idea... You can combine logically similar commands in a scenario all into one command and then execute that one.
- With a "Job Queue" you can queue different commands by another to let them get executed one after another. (Or with Logging... Similar principle)
- An invoker makes a request on the execute() method. They can be parametrized with commands and even be dynamically modified during runtime.
- Macro Commands are a simple way to allow multiple commands to be evolved.
- "Smart" command objects sometimes even implement requests by themselves rather than elabrate "hacks".
Chapter 7 - Being adaptive
- Adapters convert one thing to fit into another thing. In the Object Oriented World they let one class communicate with another completely different class.
- The client makes a request to the adapter by calling a method on it using the target interface. Then the adapter translates that request into one or more calls on the adaptee using the adaptee interface. Last, the client recieves the results of the call and never knows there is an adapter doing the translation.
- The adapter pattern converts the interface of a class into another interface the clients expect. Adapter lets classes work together that couldn't otherwise because of incompatible interfaces.
- Class adapters implement the adapter as another class by itself. Object adapters implement a certain targeted interface and then call methods from the adaptee.
- Examples besides the described variant are iterators and (old) enumerators, since you call a "next"-method for example which retrieves the needed object.
- A facade takes a very complex subsystem and then puts it into a renewable class/interface. It not only simplifies everything but also decouples everything. ๐
- Facades can wrap multiple classes to simplify complex operations.
- The facade pattern proves a unified interface to a set of interfaces in a subsystem. Facade defines a higher-level interface that makes the subsystem easier to use.
- Principle of least knowledge: Only talk to your immediate friends.
- How do you do this... Only talk to the object itself. Objects passes in as a parameter to the method. Any object the method creates or instantiates and any components of the object. It all forces us to ask an object to make a request, which is the better way...
- You can btw use more than one facade for a subsystem. ๐
- Adapters wrap objects to change the interface. A decorator adds new functionalities. FYI
Chapter 8 - Encapsulating Algorithms
- A template or a template method describes a way how you can use abstract classes and abstract methods to describe/pre-define an "abstract algorithm" and then other methods implement the missing abstract methods accordingly.
- It defines the steps of an algorithm and allows subclasses to provide the implementation for one or more steps.
- This way you can maximize reusability and the base algorithm is only at one place and lets the subclasses describe their own knowledge.
- The template method pattern defines the skeleton of an algorithm in a method deferring some steps to subclasses. Template method lets subclasses redefine certain steps of an algorithm without changing the algorithm's structure.
- A hook in that context is a method that is optional to be overriden. It has a "default" implementation that is used by the template but we can override it optionally.
- We can use those hooks really really well with test code. Just as a reminder. ๐
- The Hollywood Principle: Don't call us... We call you. -> The higher-level component should call the lower-level one.
Chapter 9 - Well-Managed Collections
- You can encapsulate loops with the use of the Iterator Pattern. It basically has a collection inside and methods to check if it has a next element as well as a method to get the next object "in the iteration".
- With an "iterator"-method you can easily create iterators of pre-existing lists. Just as an idea.
- With this you can also refer objects which are actually a list of things in a more general way.
- The Iterator Pattern provides a way to access the elements of an aggregate object sequentially without exposing its underlying representation.
- It allows the traversal of the elements of a collection without exposing the underlying implementation.
- It also allows to simplify the aggregate interface and places the responsibility where it should be.
- Single Responsibility Principle: A class should only have one reason to change. This then lets a class have high cohesion.
- You should follow this principle because if you wanna change something you should change as little things as possible. -> This avoids accidents.
- It's pretty cool btw if a collection knows how to build it's own iterator.
- The Composite Pattern allows you to compose objects into tree structures to represent part-whole hierarchies. Composite lets clients treat individual objects and compositions of objects uniformly.
- It allows us to build structures of objects in the form of trees that contain both compositions of objects and individual objects as nodes.
- We can then apply the same operations over both compositions and individual objects. In other words we can ignore the differences between objects and individual objects.
- Null Iterators are also fitting since they can not even think about doing forbidden operations like removing elements.
Chapter 10 - The State of Things
- If you need states inside your objects or other things then don't define them inside. Define special state classes which represent those states. Or even better... A state machine to handle state management.
- With this you can avoid those long nested if-else structures to handle those states.
- The state pattern allows an object to alter its behaviour when its internal state changes. The object will appear to change its class.
- Unlike a procedural state machine the state pattern represents state as full-blown classes.
- By encapsulating each state into a class we localize any changes that need to be made.
- Strategy and State Pattern are the same in a class diagram but they differ in intend. Strategy configures context classes with a behaviour or algorithm. State allows a context to change its behaviour as the state context changes.
- State classes can also be shared among context instances.
Chapter 11 - Controlling Object Access
- A remote proxy acts as a local represenative to a remote object, which lives in a different Java Virtual Machine.
- You can call local methods to that proxy and these forward them to the remote object.
- It basically acts like a local object but what really happens is that it calls methods from the remote object.
- Basically there's a client heap and a server heap. Both have helpers to call each of the either proxy/remote objects.
- How you can create an RMI remote service: Make a remote interface. Make a Remote Implementation. Generate the stubs and skeletons using rmic. Start the RMI registry. Start the remote service.
- It gets the object with the running RMI service and a URL on which the object gets hosted. Then you do a lookup on that URL. (One way is with de-/serialization and downloading that binary/text-data) Lastly you do type casting to get the final object.
- The Proxy Pattern provides a surrogate or placeholder for another object to control access to it.
- Use the Proxy Pattern to create a representative object that controls access to another object. This may be remote, expensive to create or in need of securing.
- Virtual Proxies often defer the object creation until it is actually needed. It acts as a surrogate for the object before and while it's getting created. After that it delegates to the real object.
- A Protection Proxy is a proxy which controls access to an object based on access rights.
- There are also a lot more usecases for this kind of proxy pattern. (Caching, Synchronization, Firewall...) Use it wisely. It's expensive...
- It's similar to a decorator but a decorator adds behaviour to an object while proxies control access.
Chapter 12 - Patterns of Patterns
- Patterns are often used together in the same design solution. This is called a "compound pattern".
- There are always tradeoffs when you design something. Even with "great" design patterns. Be aware of them.
- MVC (Model-View-Controller) is a very powerful compound pattern. Basically you have a view which handles all of the visual aspects. A model which handles all of the application data and logic. Lastly you have a controller which lives in the middle and handles communication and "controlling".
- In this view and controller implement a strategy pattern. (View -> Only visual strategy, delegates every logic to the controller) View is a composite. And model is an observer which keeps objects always updated.
- They work together to decouple those "three players" and to keep everything clean, separated and flexible.
- This MVC approach is also btw used in the web often... (Request-Response mechanism...)
Chapter 13 - Patterns in the Real World
- A pattern is a solution to a problem in a context.
- The context in this situation in which the pattern applies. This should be a recurring situation.
- The problem refers to the goal you are trying to achieve in this context, but it also refers to any constraints that occur in the context.
- The solution we're after is a genereal design that anyone can apply which resolves the goal and set of constraints.
- If you find yourself in a context with a problem that has a goal that is affected by a set of constraints, then you can apply a design that resolves the goal and constraints and leads to a solution.
- There are loads of patterns out there. Go out. Look at them, learn them. Do your homework.
- Try it all out. Try to "think in patterns".
- KISS (Keep it short and simple), when you refactor use a pattern, if you don't need it now... don't do it.
- Anti-Patterns tells you how to go from a problem to a BAD solution. But it looks like a good solution at first. So be aware...