/design-patterns

Brief pattern catalog based on Head First Design Patterns book.

Primary LanguageJava

Design Patterns 📚

Here's a collection of design patterns that I've implemented following the Head First Design Patterns book. My goal for this repo is to have a brief pattern catalog that I can refer to when needed.

Ilustrations are from the Refactoring Guru.

Table of contents

Creational 🔨

Creational patterns provide object creation mechanisms that increase flexibility and reuse of existing code.

Factory method

The Factory Method pattern defines an interface for creating an object, but lets subclasses decide which class to instantiate. Factory Method lets a class defer instantiation to subclasses.

By replacing new Product() with a call to createProduct(), the (abstract) superclass can operate with the object to be created. Only when a subclass is instantiated, some kind of concrete product will be actually created.

Abstract factory

The Abstract Factory pattern provides an interface for creating families of related or dependent objects without specifying their concrete classes.

Abstract factories are usually formed by a set of factory methods. By returning the abstract type of the concrete products, they decouple the client from concrete product implementations.

Singleton

The Singleton pattern lets you ensure that a class has only one instance, while providing a global access point to this instance. We can achieve that just by making the class constructor private, and adding a static method to get the unique instance (adding locks would be necessary if concurrency can happen).

Singletons can often lead to bad designs if used improperly, they are hard to test and can be problematic on multithreaded environments. So they really must be used only when needed (to access a database for example).

Builder

The Builder pattern lets you construct complex objects step by step. The pattern allows you to produce different types and representations of an object using the same construction code.

It's also useful for avoiding giant constructors and creating complex objects.

Structural 🏗️

Structural patterns explain how to assemble objects and classes into larger structures, while keeping these structures flexible and efficient.

Facade

By defining a higher-level interface, the Facade pattern provides easier access to a particular part of the subsystem’s functionality. It knows where to direct the client’s request.

Avoid creating a god object. If needed, additional facades can be made either to be used independently or to be composed with a higher-level facade (so the latter can maintain cohesiveness).

Adapter

The Adapter Pattern converts the interface of a class into another interface the clients expect. It lets classes work together that couldn’t otherwise because of incompatible interfaces.

Notice that Facade defines a new interface for existing objects, whereas Adapter tries to make the existing interface usable. Adapter usually wraps just one object, while Facade works with an entire subsystem of objects.

Decorator (or wrapper)

The Decorator Pattern attaches additional responsibilities to an object dynamically (at runtime). Decorators provide a flexible alternative to subclassing for extending functionality.

The wrapper contains the same set of methods as the target and delegates to it all requests it receives. However, the wrapper may alter the result by doing something either before or after it passes the request to the target.

Composite

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.

Proxy

The Proxy Pattern provides a surrogate or placeholder for another object to control access to it.

If you need to execute something either before or after the primary logic of the class, the proxy lets you do this without changing that class. Since the proxy implements the same interface as the original class, it can be passed to any client that expects a real service object.

Use the Proxy Pattern to create a representative object that controls access to another object, which may be remote, expensive to create, or in need of securing.

Behavioral 🗣️

Behavioral patterns take care of effective communication and assignment of responsibilities between objects.

Template method

The Template Method pattern suggests that you break down an algorithm into a series of steps, turn these steps into methods, and put a series of calls to these methods inside a single template method. The steps may either be abstract, or have some default implementation. The algorithm is completed by subclassing and overriding some optional steps if needed (but not the template method itself).

Hooks can be very useful, they are methods that give a point of extension of the algorithm, but that are not needed for the algorithm to work. An example is the onCreate() method on Android Activity, which lets the client do something before (or after) an important step.

Iterator

The main idea of the Iterator pattern is to extract the traversal behavior of a collection into a separate object called an iterator.

This makes the client code compatible with any collection type or any traversal algorithm as long as there’s a proper iterator. If you need a special way to traverse a collection, you just create a new iterator class, without having to change the collection or the client.

Strategy

The Strategy pattern suggests that you take a class that does something specific in a lot of different ways and extract all of these algorithms into separate classes called strategies.

The original class, called context, must have a field for storing a reference to one of the strategies. The context delegates the work to a linked strategy object which can be changed at runtime, and also used by other contexts.

Remember that if you only have a couple of algorithms and they rarely change, there’s no real reason to overcomplicate the program. Also keep in mind that functional typing and anonymous functions can provide the same results, avoiding the need to bloat with new classes and interfaces.

Command

Command is a behavioral design pattern that turns a request into a stand-alone object that contains all information about the request. This transformation lets you pass requests as method arguments, delay or queue a request’s execution, and support undoable operations.

All the request details should be extracted, such as the object being called, the name of the method and the list of arguments, and moved into a separate command class with a single method that triggers this request.

Chain of Responsibility, Command, Mediator and Observer address various ways of connecting senders and receivers of requests:

  • Chain of Responsibility passes a request sequentially along a dynamic chain of potential receivers until one of them handles it.

  • Command establishes unidirectional connections between senders and receivers.

  • Mediator eliminates direct connections between senders and receivers, forcing them to communicate indirectly via a mediator object.

  • Observer lets receivers dynamically subscribe to and unsubscribe from receiving requests.

Observer

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.

The list of subscribers is compiled dynamically: objects can start or stop listening to notifications at runtime, depending on the desired behavior of your app.

State

The State pattern suggests that you create new classes for all possible states of an object and extract all state-specific behaviors into these classes.

Instead of implementing all behaviors on its own, the original object, called context, stores a reference to one of the state objects that represents its current state, and delegates all the state-related work to that object.

This structure may look similar to the Strategy pattern, but there’s one key difference. In the State pattern, the particular states may be aware of each other and initiate transitions from one state to another, whereas strategies almost never know about each other.