Skip to content

PCianes/DesignPatterns-TypeScript

Repository files navigation

Design Patterns

The Catalog of Design Patterns

DesignPatterns-by-RefactoringGuru

Relations

  • Many designs start by using Factory Method (less complicated and more customizable via subclasses) and evolve toward Abstract Factory, Prototype, or Builder (more flexible, but more complicated).

  • Abstract Factory classes are often based on a set of Factory Methods, but you can also use Prototype to compose the methods on these classes.

  • You can use Factory Method along with Iterator to let collection subclasses return different types of iterators that are compatible with the collections.

  • Prototype isn’t based on inheritance, so it doesn’t have its drawbacks. On the other hand, Prototype requires a complicated initialization of the cloned object. Factory Method is based on inheritance but doesn’t require an initialization step.

  • Factory Method is a specialization of Template Method. At the same time, a Factory Method may serve as a step in a large Template Method.

  • Builder focuses on constructing complex objects step by step. Abstract Factory specializes in creating families of related objects. Abstract Factory returns the product immediately, whereas Builder lets you run some additional construction steps before fetching the product.

  • You can use Builder when creating complex Composite trees because you can program its construction steps to work recursively.

  • You can combine Builder with Bridge: the director class plays the role of the abstraction, while different builders act as implementations.

  • Abstract Factories, Builders and Prototypes can all be implemented as Singletons.

  • A Facade class can often be transformed into a Singleton since a single facade object is sufficient in most cases.

  • Flyweight would resemble Singleton if you somehow managed to reduce all shared states of the objects to just one flyweight object. But there are two fundamental differences between these patterns:

    • There should be only one Singleton instance, whereas a Flyweight class can have multiple instances with different intrinsic states.
    • The Singleton object can be mutable. Flyweight objects are immutable.
  • Abstract Factory can serve as an alternative to Facade when you only want to hide the way the subsystem objects are created from the client code.

  • You can use Abstract Factory along with Bridge. This pairing is useful when some abstractions defined by Bridge can only work with specific implementations. In this case, Abstract Factory can encapsulate these relations and hide the complexity from the client code.

  • Prototype can help when you need to save copies of Commands into history.

  • Designs that make heavy use of Composite and Decorator can often benefit from using Prototype. Applying the pattern lets you clone complex structures instead of re-constructing them from scratch.

  • Sometimes Prototype can be a simpler alternative to Memento. This works if the object, the state of which you want to store in the history, is fairly straightforward and doesn’t have links to external resources, or the links are easy to re-establish.

  • Bridge is usually designed up-front, letting you develop parts of an application independently of each other. On the other hand, Adapter is commonly used with an existing app to make some otherwise-incompatible classes work together nicely.

  • Adapter changes the interface of an existing object, while Decorator enhances an object without changing its interface. In addition, Decorator supports recursive composition, which isn’t possible when you use Adapter.

  • Adapter provides a different interface to the wrapped object, Proxy provides it with the same interface, and Decorator provides it with an enhanced interface.

  • 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.

  • Bridge, State, Strategy (and to some degree Adapter) have very similar structures. Indeed, all of these patterns are based on composition, which is delegating work to other objects. However, they all solve different problems. A pattern isn’t just a recipe for structuring your code in a specific way. It can also communicate to other developers the problem the pattern solves.

  • Chain of Responsibility is often used in conjunction with Composite. In this case, when a leaf component gets a request, it may pass it through the chain of all of the parent components down to the root of the object tree.

  • You can use Iterators to traverse Composite trees.

  • You can use Visitor to execute an operation over an entire Composite tree.

  • You can implement shared leaf nodes of the Composite tree as Flyweights to save some RAM.

  • Composite and Decorator have similar structure diagrams since both rely on recursive composition to organize an open-ended number of objects.

    • A Decorator is like a Composite but only has one child component. There’s another significant difference: Decorator adds additional responsibilities to the wrapped object, while Composite just “sums up” its children’s results.

    • However, the patterns can also cooperate: you can use Decorator to extend the behavior of a specific object in the Composite tree.

  • Flyweight shows how to make lots of little objects, whereas Facade shows how to make a single object that represents an entire subsystem.

  • Facade and Mediator have similar jobs: they try to organize collaboration between lots of tightly coupled classes.

    • Facade defines a simplified interface to a subsystem of objects, but it doesn’t introduce any new functionality. The subsystem itself is unaware of the facade. Objects within the subsystem can communicate directly.
    • Mediator centralizes communication between components of the system. The components only know about the mediator object and don’t communicate directly.
  • Facade is similar to Proxy in that both buffer a complex entity and initialize it on its own. Unlike Facade, Proxy has the same interface as its service object, which makes them interchangeable.

  • Decorator and Proxy have similar structures, but very different intents. Both patterns are built on the composition principle, where one object is supposed to delegate some of the work to another. The difference is that a Proxy usually manages the life cycle of its service object on its own, whereas the composition of Decorators is always controlled by the client.

  • Chain of Responsibility and Decorator have very similar class structures. Both patterns rely on recursive composition to pass the execution through a series of objects. However, there are several crucial differences. The CoR handlers can execute arbitrary operations independently of each other. They can also stop passing the request further at any point. On the other hand, various Decorators can extend the object’s behavior while keeping it consistent with the base interface. In addition, decorators aren’t allowed to break the flow of the request.

  • Decorator lets you change the skin of an object, while Strategy lets you change the guts.

  • 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.
  • Handlers in Chain of Responsibility can be implemented as Commands. In this case, you can execute a lot of different operations over the same context object, represented by a request. However, there’s another approach, where the request itself is a Command object. In this case, you can execute the same operation in a series of different contexts linked into a chain.

  • You can use Command and Memento together when implementing “undo”. In this case, commands are responsible for performing various operations over a target object, while mementos save the state of that object just before a command gets executed.

  • You can use Memento along with Iterator to capture the current iteration state and roll it back if necessary.

  • Template Method is based on inheritance: it lets you alter parts of an algorithm by extending those parts in subclasses. Strategy is based on composition: you can alter parts of the object’s behavior by supplying it with different strategies that correspond to that behavior. Template Method works at the class level, so it’s static. Strategy works on the object level, letting you switch behaviors at runtime.

  • Command and Strategy may look similar because you can use both to parameterize an object with some action. However, they have very different intents.

    • You can use Command to convert any operation into an object. The operation’s parameters become fields of that object. The conversion lets you defer execution of the operation, queue it, store the history of commands, send commands to remote services, etc.
    • On the other hand, Strategy usually describes different ways of doing the same thing, letting you swap these algorithms within a single context class.
  • You can treat Visitor as a powerful version of the Command pattern. Its objects can execute operations over various objects of different classes.

  • The difference between Mediator and Observer is often elusive. In most cases, you can implement either of these patterns; but sometimes you can apply both simultaneously. Let’s see how we can do that.

    • The primary goal of Mediator is to eliminate mutual dependencies among a set of system components. Instead, these components become dependent on a single mediator object. The goal of Observer is to establish dynamic one-way connections between objects, where some objects act as subordinates of others.
    • There’s a popular implementation of the Mediator pattern that relies on Observer. The mediator object plays the role of publisher, and the components act as subscribers which subscribe to and unsubscribe from the mediator’s events. When Mediator is implemented this way, it may look very similar to Observer.
    • When you’re confused, remember that you can implement the Mediator pattern in other ways. For example, you can permanently link all the components to the same mediator object. This implementation won’t resemble Observer but will still be an instance of the Mediator pattern.
    • Now imagine a program where all components have become publishers, allowing dynamic connections between each other. There won’t be a centralized mediator object, only a distributed set of observers.
  • You can use Visitor along with Iterator to traverse a complex data structure and execute some operation over its elements, even if they all have different classes.

  • State can be considered as an extension of Strategy. Both patterns are based on composition: they change the behavior of the context by delegating some work to helper objects. Strategy makes these objects completely independent and unaware of each other. However, State doesn’t restrict dependencies between concrete states, letting them alter the state of the context at will.

About

Examples of Design Patterns with TypeScript

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published