Design patterns[refactoring guru]

CBS [22]

  • Creational [5] —

    • provides various object creation mechanism which increase flexibility and reuse of existing code

    • Types

      • singleton -

        • Ensure that a class has only one instance while providing global access point to this instance.

      • factory -

        • Provides an interface (using abstract class) for creating object in superclass but allows subclasses to alter the type of objects that will be created.

      • abstract factory -

        • Produce family of related objects without specifying their concrete classes.

      • builder -

        • Construct complex objects step by step.

        • this allows us to produce different types and representations of a object using the same construction code.

      • prototype -

        • lets us copy existing object without making code dependent on their classes

  • Behavioural [11]

    • Patterns concerned with algorithms and the assignments of responsibilities between objects.

      • Observer

        • strategy -

          • lets us define a familty of alorithms, put each of them into a seperate class, and make their objects interchangable.

        • command —

          • Turns a request into a stand alone object that contains all information about the request.

          • this transformation lets us pass requests as a method arguments, delay or queue a requests execution and support undoable operations.

        • state--

          • lets an object alter its behaviour when its internal state changes

          • it appears if the object changed its class

        • visitor --

          • lets us separate algorithms from the objects on which they operate

        • momento

          • lets us save and restore the previous state of an object without revealing the details of its implementations

        • iterator --

          • Lets us traverse elements of a collection without exposing its underlying representation (list, stack, tree etc)

        • mediator

          • lets us reduce chaotic dependencies between objects/

          • the patterns restricts direct communications between the objects and forces them to collaborate only via a mediator object

        • chain of responsibility --

          • Lets us pass request along a chain of handlers, upon receiving a request, each handler decides either to process the request or to pass it to the next handler in the chain.

  • structural [7]

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

      • adapter

        • bridge

          • lets us split a large class or a set of closely related classes into two seperate hierarchies- abstraction and implementation which can be developed independently of each other.

        • composite

          • lets us compose objects into tree structure and then work with these structures as if they were individual objects.

        • decorator

          • lets us attach new behaviours to objects by placing these objects inside special wrapper objects that contain the behaviour

        • facade

          • provides a simplified interface to a library, a framework or any other complex set of classes.

        • flywheel

          • lets us fit more object into the same available amount of ram by sharing common parts of state between multiple objects instead of keeping all of the data in each object.

        • proxy

          • lets us provide a substitute or placeholder for another object.

          • A proxy controls access to the original object, allowing us to perform something either before or after the request gets through to the original object.

Creational

Singleton

  • Intent

    • lets us ensure that a class has only one instance whule providing a global access point to this instance.

  • problem it solves

    • Ensure that a class has just a single instance

      • One might need a single instance of a class to control access to some shared resource like a database or a file

      • ex — If a object is already created, instead of creating a fresh object we will get the one we already created.

    • Provide a global access point to that instance

      • Global variables with are used to store som essential values can be unsage as they can be overwritten.

      • just like a global variable the singleton patterns lets us access the object from anywhere in the program but it also protects that instance from being overwritten by other code

  • Solution it provides

    • Make the default constructor private, to prevent other objects from using the new operator with the singleton class

    • Create a static creation method that acts as a constructor, under the hood this method calls the private constructor to create an object and saves it in a static fiels.

    • All following calls to this method returns the cached objects.

  • real world analogy --

    • govt is an excellent example of the singleton pattern

    • a county can have only one official govt.

    • Regardless of the personal iddentities of the individual who for the govt.

  • Structure

  • applicability

    • use the singleton pattern when a class in the program should have just a single instance available to all clients

    • ex — a single database object shared by different parts of the program

    • the singleton pattern disables all other means of creating objects of a class except for the spl creation method.

    • This method either creates a new object or returns an existing one if it has already been created.

    • use the singleton patterns when one needd strictier control over global variables

    • unlike global variables the singleton pattern guaranttes that there is just one instance of the class.

    • nothing except the singleton class itself can replace the cashed instance

  • How to implement

    • add a private static filed to the class for storing singleton instance

    • declare a public static creation method for creating the singleton instance

    • implement "lazy initialization" inside the static method. it should create a new object on its first call and put it into the static field

    • The method should always return that instance on all subsequent calls.

    • Make the constructor of the class private, the static method of the class with stil be able to call the constructor but not the other objects

    • Go over the client code and replace all direct calls to the singleton constructor with calls to its static creation method

  • Pros

    • one can be sure that a class has only a sinlge instance

    • one gain a global access point to that instance

    • the singleton object is initialized only when its requested for the first time

  • Cons

    • violates the sinlge responsibility principle, the patterns solves two problems at a time

    • can mask bad design, for instance when componenets of the program know too much about each other

    • pattern requires special tratment in a multithreaded environment so that multiple threads won't create a singleton object several times.

    • It may be difficult to unit test the client code of the singleton because many test frameworks rely on inheritance when producing mock objects

    • Since the constructor of the singleton class is provate and overriding static methods is impossible in most languages one will need to think of a creative way to mock the singleton or just don't write the tests

  • Relation with other patterns

Factory

  • Intent

    • provides an interface for creating objects in a superclass, but allows subclasses to alter the type of objects that will be created.

  • Problem

    • lets say we have a transportation code where most of the code is for trucks now we want to add ships as well, but this would require making changes to the entire codebase.

    • This would lead to a pretty nasty code over time.

  • Solution

    • Factory Method pattern suggests that you replace direct object construction calls (using the new operator) with calls to a special factory method.

    • Don’t worry: the objects are still created via the new operator, but it’s being called from within the factory method. Objects returned by a factory method are often referred to as products.

    • At first glance, this change may look pointless: we just moved the constructor call from one part of the program to another.

    • However, consider this: now you can override the factory method in a subclass and change the class of products being created by the method.

    • For example, both Truck and Ship classes should implement the Transport interface, which declares a method called deliver.

    • Each class implements this method differently: trucks deliver cargo by land, ships deliver cargo by sea. The factory method in the RoadLogistics class returns truck objects, whereas the factory method in the SeaLogistics class returns ships.

    • The code that uses the factory method (often called the client code) doesn’t see a difference between the actual products returned by various subclasses.

    • The client treats all the products as abstract Transport. The client knows that all transport objects are supposed to have the deliver method, but exactly how it works isn’t important to the client.

  • Structure

    • The Product declares the interface, which is common to all objects that can be produced by the creator and its subclasses.

    • Concrete Products are different implementations of the product interface

    • The Creator class declares the factory method that returns new product objects. It’s important that the return type of this method matches the product interface

    • Concrete Creators override the base factory method so it returns a different type of product

  • Apllicability

    • Use the Factory Method when you don’t know beforehand the exact types and dependencies of the objects your code should work with.

    • The Factory Method separates product construction code from the code that actually uses the product. Therefore it’s easier to extend the product construction code independently from the rest of the code.

    • For example, to add a new product type to the app, you’ll only need to create a new creator subclass and override the factory method in it.

    • Use the Factory Method when you want to provide users of your library or framework with a way to extend its internal components.

    • Inheritance is probably the easiest way to extend the default behavior of a library or framework. But how would the framework recognize that your subclass should be used instead of a standard component?

    • The solution is to reduce the code that constructs components across the framework into a single factory method and let anyone override this method in addition to extending the component itself.

    • Let’s see how that would work. Imagine that you write an app using an open source UI framework. Your app should have round buttons, but the framework only provides square ones.

    • You extend the standard Button class with a glorious RoundButton subclass. But now you need to tell the main UIFramework class to use the new button subclass instead of a default one.

    • To achieve this, you create a subclass UIWithRoundButtons from a base framework class and override its createButton method.

    • While this method returns Button objects in the base class, you make your subclass return RoundButton objects.

    • Now use the UIWithRoundButtons class instead of UIFramework. And that’s about it!

    • Use the Factory Method when you want to save system resources by reusing existing objects instead of rebuilding them each time.

    • You often experience this need when dealing with large, resource-intensive objects such as database connections, file systems, and network resources.

    • Let’s think about what has to be done to reuse an existing object:

      1. First, you need to create some storage to keep track of all of the created objects.

      2. When someone requests an object, the program should look for a free object inside that pool.

      3. … and then return it to the client code.

      4. If there are no free objects, the program should create a new one (and add it to the pool).

    • That’s a lot of code! And it must all be put into a single place so that you don’t pollute the program with duplicate code.

    • Probably the most obvious and convenient place where this code could be placed is the constructor of the class whose objects we’re trying to reuse.

    • However, a constructor must always return new objects by definition. It can’t return existing instances.

    • Therefore, you need to have a regular method capable of creating new objects as well as reusing existing ones. That sounds very much like a factory method.

  • How to implement

    • Make all products follow the same interface. This interface should declare methods that make sense in every product

    • Add an empty factory method inside the creator class. The return type of the method should match the common product interface

    • In the creator’s code find all references to product constructors. One by one, replace them with calls to the factory method, while extracting the product creation code into the factory method

    • You might need to add a temporary parameter to the factory method to control the type of returned product

    • At this point, the code of the factory method may look pretty ugly. It may have a large switch statement that picks which product class to instantiate. But don’t worry, we’ll fix it soon enough

    • Now, create a set of creator subclasses for each type of product listed in the factory method. Override the factory method in the subclasses and extract the appropriate bits of construction code from the base method

    • If there are too many product types and it doesn’t make sense to create subclasses for all of them, you can reuse the control parameter from the base class in subclasses

    • If, after all of the extractions, the base factory method has become empty, you can make it abstract. If there’s something left, you can make it a default behavior of the method

  • Pros

    • You avoid tight coupling between the creator and the concrete products

    • Single Responsibility Principle. You can move the product creation code into one place in the program, making the code easier to support

    • Open/Closed Principle. You can introduce new types of products into the program without breaking existing client code

  • Cons

    • The code may become more complicated since you need to introduce a lot of new subclasses to implement the pattern. The best case scenario is when you’re introducing the pattern into an existing hierarchy of creator classes

  • Relation with other pattern

Abstract Factory

  • intent --

    • lets us produce family of related object without specifying their concrete classes

  • Problem

    • lets say we have a furniture shop simulator, where

      • A family of related products, say: Chair + Sofa + CoffeeTable

      • Several variants of this family. For example, products Chair + Sofa + CoffeeTable are available in these variants: Modern, Victorian, ArtDeco

      • You need a way to create individual furniture objects so that they match other objects of the same family. Customers get quite mad when they receive non-matching furniture

      • Also, you don’t want to change existing code when adding new products or families of products to the program. Furniture vendors update their catalogs very often, and you wouldn’t want to change the core code each time it happens.

  • Solution --

    • explicitly declare interfaces for each distinct product of the product family (e.g., chair, sofa or coffee table).

    • Then you can make all variants of products follow those interfaces.

    • For example, all chair variants can implement the Chair interface; all coffee table variants can implement the CoffeeTable interface, and so on.

    • The next move is to declare the Abstract Factory—an interface with a list of creation methods for all products that are part of the product family (for example, createChair, createSofa and createCoffeeTable).

    • These methods must return abstract product types represented by the interfaces we extracted previously: Chair, Sofa, CoffeeTable and so on.

    • Now, how about the product variants? For each variant of a product family, we create a separate factory class based on the AbstractFactory interface. A factory is a class that returns products of a particular kind.

    • For example, the ModernFurnitureFactory can only create ModernChair, ModernSofa and ModernCoffeeTable objects.

    • The client code has to work with both factories and products via their respective abstract interfaces.

    • This lets you change the type of a factory that you pass to the client code, as well as the product variant that the client code receives, without breaking the actual client code.

    • Say the client wants a factory to produce a chair. The client doesn’t have to be aware of the factory’s class, nor does it matter what kind of chair it gets.

    • Whether it’s a Modern model or a Victorian-style chair, the client must treat all chairs in the same manner, using the abstract Chair interface.

    • With this approach, the only thing that the client knows about the chair is that it implements the sitOn method in some way.

    • Also, whichever variant of the chair is returned, it’ll always match the type of sofa or coffee table produced by the same factory object.

    • There’s one more thing left to clarify: if the client is only exposed to the abstract interfaces, what creates the actual factory objects?

    • Usually, the application creates a concrete factory object at the initialization stage. Just before that, the app must select the factory type depending on the configuration or the environment settings.

  • Structure

    • Abstract Products declare interfaces for a set of distinct but related products which make up a product family

    • Concrete Products are various implementations of abstract products, grouped by variants. Each abstract product (chair/sofa) must be implemented in all given variants (Victorian/Modern

    • The Abstract Factory interface declares a set of methods for creating each of the abstract products.

    • Concrete Factories implement creation methods of the abstract factory. Each concrete factory corresponds to a specific variant of products and creates only those product variants.

    • Although concrete factories instantiate concrete products, signatures of their creation methods must return corresponding abstract products.

    • This way the client code that uses a factory doesn’t get coupled to the specific variant of the product it gets from a factory. The Client can work with any concrete factory/product variant, as long as it communicates with their objects via abstract interfaces.

  • Applicability --

    • Use the Abstract Factory when your code needs to work with various families of related products, but you don’t want it to depend on the concrete classes of those products—they might be unknown beforehand or you simply want to allow for future extensibility

    • The Abstract Factory provides you with an interface for creating objects from each class of the product family. As long as your code creates objects via this interface, you don’t have to worry about creating the wrong variant of a product which doesn’t match the products already created by your app.

    • Consider implementing the Abstract Factory when you have a class with a set of Factory Methodsarrow-up-right that blur its primary responsibility

    • In a well-designed program each class is responsible only for one thing. When a class deals with multiple product types, it may be worth extracting its factory methods into a stand-alone factory class or a full-blown Abstract Factory implementation

  • How to implement --

    • Map out a matrix of distinct product types versus variants of these products.

    • Declare abstract product interfaces for all product types. Then make all concrete product classes implement these interfaces.

    • Declare the abstract factory interface with a set of creation methods for all abstract products.

    • Implement a set of concrete factory classes, one for each product variant.

    • Create factory initialization code somewhere in the app. It should instantiate one of the concrete factory classes, depending on the application configuration or the current environment. Pass this factory object to all classes that construct products.

    • Scan through the code and find all direct calls to product constructors. Replace them with calls to the appropriate creation method on the factory object.

  • Pros

    • You can be sure that the products you’re getting from a factory are compatible with each other

    • You avoid tight coupling between concrete products and client code.

    • Single Responsibility Principle. You can extract the product creation code into one place, making the code easier to support

    • Open/Closed Principle. You can introduce new variants of products without breaking existing client code.

  • Cons

    • The code may become more complicated than it should be, since a lot of new interfaces and classes are introduced along with the pattern.

  • Relation with other patterns

  • Code

Builder

  • Intent

    • construct complex objects step by step.

    • The pattern allows you to produce different types and representations of an object using the same construction code.

  • Problem

    • Imagine a complex object that requires laborious, step-by-step initialisation of many fields and nested objects.

    • Such initialisation code is usually buried inside a monstrous constructor with lots of parameters. Or even worse: scattered all over the client code.

    • For example, let’s think about how to create a House object. To build a simple house, you need to construct four walls and a floor, install a door, fit a pair of windows, and build a roof. But what if you want a bigger, brighter house, with a backyard and other goodies (like a heating system, plumbing, and electrical wiring)?

    • The simplest solution is to extend the base House class and create a set of subclasses to cover all combinations of the parameters.

    • But eventually you’ll end up with a considerable number of subclasses. Any new parameter, such as the porch style, will require growing this hierarchy even more.

    • There’s another approach that doesn’t involve breeding subclasses.

    • You can create a giant constructor right in the base House class with all possible parameters that control the house object. While this approach indeed eliminates the need for subclasses, it creates another problem.

    • In most cases most of the parameters will be unused, making the constructor calls pretty uglyarrow-up-right. For instance, only a fraction of houses have swimming pools, so the parameters related to swimming pools will be useless nine times out of ten.

  • Solution

    • Builder pattern suggests that you extract the object construction code out of its own class and move it to separate objects called builders

    • The pattern organizes object construction into a set of steps (buildWalls, buildDoor, etc.). To create an object, you execute a series of these steps on a builder object.

    • The important part is that you don’t need to call all of the steps. You can call only those steps that are necessary for producing a particular configuration of an object.

    • Some of the construction steps might require different implementation when you need to build various representations of the product.

    • For example, walls of a cabin may be built of wood, but the castle walls must be built with stone.

    • In this case, you can create several different builder classes that implement the same set of building steps, but in a different manner.

    • Then you can use these builders in the construction process (i.e., an ordered set of calls to the building steps) to produce different kinds of objects.

    • For example, imagine a builder that builds everything from wood and glass, a second one that builds everything with stone and iron and a third one that uses gold and diamonds.

    • By calling the same set of steps, you get a regular house from the first builder, a small castle from the second and a palace from the third.

    • However, this would only work if the client code that calls the building steps is able to interact with builders using a common interface.

  • Director

    • You can go further and extract a series of calls to the builder steps you use to construct a product into a separate class called director.

    • The director class defines the order in which to execute the building steps, while the builder provides the implementation for those steps.

    • Having a director class in your program isn’t strictly necessary. You can always call the building steps in a specific order directly from the client code.

    • However, the director class might be a good place to put various construction routines so you can reuse them across your program.

    • In addition, the director class completely hides the details of product construction from the client code.

    • The client only needs to associate a builder with a director, launch the construction with the director, and get the result from the builder.

  • Structure

    • The Builder interface declares product construction steps that are common to all types of builders.

    • Concrete Builders provide different implementations of the construction steps. Concrete builders may produce products that don’t follow the common interface.

    • Products are resulting objects. Products constructed by different builders don’t have to belong to the same class hierarchy or interface.

    • The Director class defines the order in which to call construction steps, so you can create and reuse specific configurations of products.

    • The Client must associate one of the builder objects with the director. Usually, it’s done just once, via parameters of the director’s constructor. Then the director uses that builder object for all further construction.

    • However, there’s an alternative approach for when the client passes the builder object to the production method of the director. In this case, you can use a different builder each time you produce something with the director.

  • Applicability --

    • Use the Builder pattern to get rid of a “telescoping constructor”.

    • Say you have a constructor with ten optional parameters. Calling such a beast is very inconvenient; therefore, you overload the constructor and create several shorter versions with fewer parameters. These constructors still refer to the main one, passing some default values into any omitted parameters

    • The Builder pattern lets you build objects step by step, using only those steps that you really need. After implementing the pattern, you don’t have to cram dozens of parameters into your constructors anymore.

    • Use the Builder pattern when you want your code to be able to create different representations of some product (for example, stone and wooden houses).

    • The Builder pattern can be applied when construction of various representations of the product involves similar steps that differ only in the details.

    • The base builder interface defines all possible construction steps, and concrete builders implement these steps to construct particular representations of the product. Meanwhile, the director class guides the order of construction.

    • Use the Builder to construct Compositearrow-up-right trees or other complex objects.

    • The Builder pattern lets you construct products step-by-step. You could defer execution of some steps without breaking the final product. You can even call steps recursively, which comes in handy when you need to build an object tree.

    • A builder doesn’t expose the unfinished product while running construction steps. This prevents the client code from fetching an incomplete result.

  • How to implement --

    • Make sure that you can clearly define the common construction steps for building all available product representations. Otherwise, you won’t be able to proceed with implementing the pattern.

    • Declare these steps in the base builder interface.

    • Create a concrete builder class for each of the product representations and implement their construction steps.

    • Don’t forget about implementing a method for fetching the result of the construction. The reason why this method can’t be declared inside the builder interface is that various builders may construct products that don’t have a common interface.

    • Therefore, you don’t know what would be the return type for such a method. However, if you’re dealing with products from a single hierarchy, the fetching method can be safely added to the base interface.

    • Think about creating a director class. It may encapsulate various ways to construct a product using the same builder object.

    • The client code creates both the builder and the director objects. Before construction starts, the client must pass a builder object to the director. Usually, the client does this only once, via parameters of the director’s class constructor. The director uses the builder object in all further construction. There’s an alternative approach, where the builder is passed to a specific product construction method of the director.

    • The construction result can be obtained directly from the director only if all products follow the same interface. Otherwise, the client should fetch the result from the builder.

  • Pros

    • You can construct objects step-by-step, defer construction steps or run steps recursively.

    • You can reuse the same construction code when building various representations of products.

    • Single Responsibility Principle. You can isolate complex construction code from the business logic of the product.

  • Cons

    • The overall complexity of the code increases since the pattern requires creating multiple new classes.

  • Relations with other patterns

  • Code

Prototype

  • Intent

    • lets you copy existing objects without making your code dependent on their classes.

  • Problem

    • Say you have an object, and you want to create an exact copy of it. How would you do it? First, you have to create a new object of the same class.

    • Then you have to go through all the fields of the original object and copy their values over to the new object.

    • Nice! But there’s a catch. Not all objects can be copied that way because some of the object’s fields may be private and not visible from outside of the object itself.

    • There’s one more problem with the direct approach. Since you have to know the object’s class to create a duplicate, your code becomes dependent on that class.

    • If the extra dependency doesn’t scare you, there’s another catch.

    • Sometimes you only know the interface that the object follows, but not its concrete class, when, for example, a parameter in a method accepts any objects that follow some interface.

  • Solution

    • delegates the cloning process to the actual objects that are being cloned.

    • The pattern declares a common interface for all objects that support cloning.

    • This interface lets you clone an object without coupling your code to the class of that object. Usually, such an interface contains just a single clone method.

    • The implementation of the clone method is very similar in all classes.

    • The method creates an object of the current class and carries over all of the field values of the old object into the new one.

    • You can even copy private fields because most programming languages let objects access private fields of other objects that belong to the same class.

    • An object that supports cloning is called a prototype. When your objects have dozens of fields and hundreds of possible configurations, cloning them might serve as an alternative to subclassing.

    • Here’s how it works: you create a set of objects, configured in various ways. When you need an object like the one you’ve configured, you just clone a prototype instead of constructing a new object from scratch.

  • Real world analogy

    • real life, prototypes are used for performing various tests before starting mass production of a product.

    • However, in this case, prototypes don’t participate in any actual production, playing a passive role instead.

    • Since industrial prototypes don’t really copy themselves, a much closer analogy to the pattern is the process of mitotic cell division (biology, remember?).

    • After mitotic division, a pair of identical cells is formed. The original cell acts as a prototype and takes an active role in creating the copy.

  • Structure

    • The Prototype interface declares the cloning methods. In most cases, it’s a single clone method.

    • The Concrete Prototype class implements the cloning method. In addition to copying the original object’s data to the clone, this method may also handle some edge cases of the cloning process related to cloning linked objects, untangling recursive dependencies, etc.

    • The Client can produce a copy of any object that follows the prototype interface

    • Prototype registry implementation

      • Prototype Registry provides an easy way to access frequently-used prototypes. It stores a set of pre-built objects that are ready to be copied.

      • The simplest prototype registry is a name → prototype hash map. However, if you need better search criteria than a simple name, you can build a much more robust version of the registry

  • Aplicability

    • Use the Prototype pattern when your code shouldn’t depend on the concrete classes of objects that you need to copy

    • This happens a lot when your code works with objects passed to you from 3rd-party code via some interface. The concrete classes of these objects are unknown, and you couldn’t depend on them even if you wanted to.

    • The Prototype pattern provides the client code with a general interface for working with all objects that support cloning. This interface makes the client code independent from the concrete classes of objects that it clones.

    • Use the pattern when you want to reduce the number of subclasses that only differ in the way they initialize their respective objects

    • Suppose you have a complex class that requires a laborious configuration before it can be used.

    • There are several common ways to configure this class, and this code is scattered through your app.

    • To reduce the duplication, you create several subclasses and put every common configuration code into their constructors. You solved the duplication problem, but now you have lots of dummy subclasses.

    • The Prototype pattern lets you use a set of pre-built objects configured in various ways as prototypes. Instead of instantiating a subclass that matches some configuration, the client can simply look for an appropriate prototype and clone it.

  • How to implement

    • Create the prototype interface and declare the clone method in it. Or just add the method to all classes of an existing class hierarchy, if you have one.

    • A prototype class must define the alternative constructor that accepts an object of that class as an argument. The constructor must copy the values of all fields defined in the class from the passed object into the newly created instance. If you’re changing a subclass, you must call the parent constructor to let the superclass handle the cloning of its private fields.

    • If your programming language doesn’t support method overloading, you won’t be able to create a separate “prototype” constructor. Thus, copying the object’s data into the newly created clone will have to be performed within the clone method. Still, having this code in a regular constructor is safer because the resulting object is returned fully configured right after you call the new operator.

    • The cloning method usually consists of just one line: running a new operator with the prototypical version of the constructor. Note, that every class must explicitly override the cloning method and use its own class name along with the new operator. Otherwise, the cloning method may produce an object of a parent class.

    • Optionally, create a centralized prototype registry to store a catalog of frequently used prototypes.

    • You can implement the registry as a new factory class or put it in the base prototype class with a static method for fetching the prototype. This method should search for a prototype based on search criteria that the client code passes to the method. The criteria might either be a simple string tag or a complex set of search parameters. After the appropriate prototype is found, the registry should clone it and return the copy to the client.

  • Pros

    • You can clone objects without coupling to their concrete classes.

    • You can get rid of repeated initialization code in favor of cloning pre-built prototypes.

    • You can produce complex objects more conveniently.

    • You get an alternative to inheritance when dealing with configuration presets for complex objects.

  • Cons

    • Cloning complex objects that have circular references might be very tricky

  • Relations with other patterns

  • Code

Structural Patterns

Adapter

  • structural design pattern that allows objects with incompatible interfaces to collablorate

  • Problem

    • creating a stock market monitoring app

    • app downloads the stock data from multiple sources in XML format and then displays nice-looking charts and diagrams for the user.

    • At some point, you decide to improve the app by integrating a smart 3rd-party analytics library.

    • But there’s a catch: the analytics library only works with data in JSON format.

    • One might think to change the library to work with XML, but this might break some existing code that relies on the library or worse we might not have access to the library source code in the first place making this approach impossible

  • Solution

    • We can create an adapter, this spl object converts the interface of one object so that another object can understand it.

    • An adapter wraps one of the objects to hide the complexity of conversion happening behind the scenes.

    • The wrapped object isn’t even aware of the adapter. For example, you can wrap an object that operates in meters and kilometers with an adapter that converts all of the data to imperial units such as feet and miles.

    • Adapters can not only convert data into various formats but can also help objects with different interfaces collaborate. Here’s how it works:

      1. The adapter gets an interface, compatible with one of the existing objects.

      2. Using this interface, the existing object can safely call the adapter’s methods.

      3. Upon receiving a call, the adapter passes the request to the second object, but in a format and order that the second object expects.

    • Sometimes it’s even possible to create a two-way adapter that can convert the calls in both directions.

    • To solve the dilemma of incompatible formats, you can create XML-to-JSON adapters for every class of the analytics library that your code works with directly.

    • Then you adjust your code to communicate with the library only via these adapters. When an adapter receives a call, it translates the incoming XML data into a JSON structure and passes the call to the appropriate methods of a wrapped analytics object.

  • Real world analogy

    • Power adapter plug

  • Structure

    • Object adapter

      • This implementation uses the object composition principle: the adapter implements the interface of one object and wraps the other one.

      • It can be implemented in all popular programming languages.

        1. The Client is a class that contains the existing business logic of the program.

        2. The Client Interface describes a protocol that other classes must follow to be able to collaborate with the client code.

        3. The Service is some useful class (usually 3rd-party or legacy). The client can’t use this class directly because it has an incompatible interface.

        4. The Adapter is a class that’s able to work with both the client and the service: it implements the client interface, while wrapping the service object. The adapter receives calls from the client via the client interface and translates them into calls to the wrapped service object in a format it can understand.

        5. The client code doesn’t get coupled to the concrete adapter class as long as it works with the adapter via the client interface. Thanks to this, you can introduce new types of adapters into the program without breaking the existing client code. This can be useful when the interface of the service class gets changed or replaced: you can just create a new adapter class without changing the client code.

    • Class adapter

      • This implementation uses inheritance: the adapter inherits interfaces from both objects at the same time.

      • Note that this approach can only be implemented in programming languages that support multiple inheritance, such as C++

        1. The Class Adapter doesn’t need to wrap any objects because it inherits behaviors from both the client and the service. The adaptation happens within the overridden methods. The resulting adapter can be used in place of an existing client class.

    • Applicability

      • Use the Adapter class when you want to use some existing class, but its interface isn’t compatible with the rest of your code.

      • The Adapter pattern lets you create a middle-layer class that serves as a translator between your code and a legacy class, a 3rd-party class or any other class with a weird interface.

      • Use the pattern when you want to reuse several existing subclasses that lack some common functionality that can’t be added to the superclass.

      • You could extend each subclass and put the missing functionality into new child classes. However, you’ll need to duplicate the code across all of these new classes, which smells really badarrow-up-right.

      • The much more elegant solution would be to put the missing functionality into an adapter class. Then you would wrap objects with missing features inside the adapter, gaining needed features dynamically. For this to work, the target classes must have a common interface, and the adapter’s field should follow that interface. This approach looks very similar to the Decoratorarrow-up-right pattern.

    • How to implement

      • Make sure that you have at least two classes with incompatible interfaces:

        • A useful service class, which you can’t change (often 3rd-party, legacy or with lots of existing dependencies).

        • One or several client classes that would benefit from using the service class.

      • Create the adapter class and make it follow the client interface. Leave all the methods empty for now.

      • Add a field to the adapter class to store a reference to the service object. The common practice is to initialize this field via the constructor, but sometimes it’s more convenient to pass it to the adapter when calling its methods.

      • One by one, implement all methods of the client interface in the adapter class. The adapter should delegate most of the real work to the service object, handling only the interface or data format conversion.

      • Clients should use the adapter via the client interface. This will let you change or extend the adapters without affecting the client code.

      • Declare the client interface and describe how clients communicate with the service.

    • Pros

      • Single Responsibility Principle. You can separate the interface or data conversion code from the primary business logic of the program.

      • Open/Closed Principle. You can introduce new types of adapters into the program without breaking the existing client code, as long as they work with the adapters through the client interface.

    • Cons

      • The overall complexity of the code increases because you need to introduce a set of new interfaces and classes. Sometimes it’s simpler just to change the service class so that it matches the rest of your code.fw

    • Relations with other patterns

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

      • Adapterarrow-up-right provides a completely different interface for accessing an existing object. On the other hand, with the Decoratorarrow-up-right pattern the interface either stays the same or gets extended. In addition, Decorator supports recursive composition, which isn’t possible when you use Adapter.

      • With Adapterarrow-up-right you access an existing object via different interface. With Proxyarrow-up-right, the interface stays the same. With Decoratorarrow-up-right you access the object via an enhanced interface.

      • Facadearrow-up-right defines a new interface for existing objects, whereas Adapterarrow-up-right tries to make the existing interface usable. Adapter usually wraps just one object, while Facade works with an entire subsystem of objects.

      • Bridgearrow-up-right, Statearrow-up-right, Strategyarrow-up-right (and to some degree Adapterarrow-up-right) 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.

    • Code

Bridge

  • lets you split a large class or a set of closely related classes into two separate hierarchies—abstraction and implementation—which can be developed independently of each other.

  • Problem

    • Say you have a geometric Shape class with a pair of subclasses: Circle and Square. You want to extend this class hierarchy to incorporate colors, so you plan to create Red and Blue shape subclasses. However, since you already have two subclasses, you’ll need to create four class combinations such as BlueCircle and RedSquare

    • Adding new shape types and colors to the hierarchy will grow it exponentially. For example, to add a triangle shape you’d need to introduce two subclasses, one for each color. And after that, adding a new color would require creating three subclasses, one for each shape type. The further we go, the worse it becomes.

  • Solution

    • occurs because we’re trying to extend the shape classes in two independent dimensions: by form and by color. That’s a very common issue with class inheritance.

    • Bridge pattern attempts to solve this problem by switching from inheritance to the object composition.

    • What this means is that you extract one of the dimensions into a separate class hierarchy, so that the original classes will reference an object of the new hierarchy, instead of having all of its state and behaviors within one class.

    • Following this approach, we can extract the color-related code into its own class with two subclasses: Red and Blue.

    • The Shape class then gets a reference field pointing to one of the color objects. Now the shape can delegate any color-related work to the linked color object.

    • That reference will act as a bridge between the Shape and Color classes. From now on, adding new colors won’t require changing the shape hierarchy, and vice versa.

    • Abstraction and implementation

      • The GoF book info-circle introduces the terms Abstraction and Implementation as part of the Bridge definition. In my opinion, the terms sound too academic and make the pattern seem more complicated than it really is. Having read the simple example with shapes and colors, let’s decipher the meaning behind the GoF book’s scary words.

      • Abstraction (also called interface) is a high-level control layer for some entity. This layer isn’t supposed to do any real work on its own. It should delegate the work to the implementation layer (also called platform).

      • Note that we’re not talking about interfaces or abstract classes from your programming language. These aren’t the same things.

      • When talking about real applications, the abstraction can be represented by a graphical user interface (GUI), and the implementation could be the underlying operating system code (API) which the GUI layer calls in response to user interactions.

      • Generally speaking, you can extend such an app in two independent directions:

        • Have several different GUIs (for instance, tailored for regular customers or admins).

        • Support several different APIs (for example, to be able to launch the app under Windows, Linux, and macOS).

      • In a worst-case scenario, this app might look like a giant spaghetti bowl, where hundreds of conditionals connect different types of GUI with various APIs all over the code.

      • You can bring order to this chaos by extracting the code related to specific interface-platform combinations into separate classes. However, soon you’ll discover that there are lots of these classes.

      • The class hierarchy will grow exponentially because adding a new GUI or supporting a different API would require creating more and more classes.

      • Let’s try to solve this issue with the Bridge pattern. It suggests that we divide the classes into two hierarchies:

        • Abstraction: the GUI layer of the app.

        • Implementation: the operating systems’ APIs.

      • The abstraction object controls the appearance of the app, delegating the actual work to the linked implementation object.

      • Different implementations are interchangeable as long as they follow a common interface, enabling the same GUI to work under Windows and Linux.

      • As a result, you can change the GUI classes without touching the API-related classes. Moreover, adding support for another operating system only requires creating a subclass in the implementation hierarchy.

  • Structure

      1. The Abstraction provides high-level control logic. It relies on the implementation object to do the actual low-level work.

      2. The Implementation declares the interface that’s common for all concrete implementations. An abstraction can only communicate with an implementation object via methods that are declared here.

        The abstraction may list the same methods as the implementation, but usually the abstraction declares some complex behaviors that rely on a wide variety of primitive operations declared by the implementation.

      3. Concrete Implementations contain platform-specific code.

      4. Refined Abstractions provide variants of control logic. Like their parent, they work with different implementations via the general implementation interface.

      5. Usually, the Client is only interested in working with the abstraction. However, it’s the client’s job to link the abstraction object with one of the implementation objects.

  • Applicability

    • Use the Bridge pattern when you want to divide and organize a monolithic class that has several variants of some functionality (for example, if the class can work with various database servers).

    • The bigger a class becomes, the harder it is to figure out how it works, and the longer it takes to make a change. The changes made to one of the variations of functionality may require making changes across the whole class, which often results in making errors or not addressing some critical side effects.

    • The Bridge pattern lets you split the monolithic class into several class hierarchies. After this, you can change the classes in each hierarchy independently of the classes in the others. This approach simplifies code maintenance and minimizes the risk of breaking existing code.

    • Use the pattern when you need to extend a class in several orthogonal (independent) dimensions.

    • The Bridge suggests that you extract a separate class hierarchy for each of the dimensions. The original class delegates the related work to the objects belonging to those hierarchies instead of doing everything on its own.

    • Use the Bridge if you need to be able to switch implementations at runtime.

    • Although it’s optional, the Bridge pattern lets you replace the implementation object inside the abstraction. It’s as easy as assigning a new value to a field.

    • By the way, this last item is the main reason why so many people confuse the Bridge with the Strategyarrow-up-right pattern. Remember that a pattern is more than just a certain way to structure your classes. It may also communicate intent and a problem being addressed.

  • How to implement

    1. Identify the orthogonal dimensions in your classes. These independent concepts could be: abstraction/platform, domain/infrastructure, front-end/back-end, or interface/implementation.

    2. See what operations the client needs and define them in the base abstraction class.

    3. Determine the operations available on all platforms. Declare the ones that the abstraction needs in the general implementation interface.

    4. For all platforms in your domain create concrete implementation classes, but make sure they all follow the implementation interface.

    5. Inside the abstraction class, add a reference field for the implementation type. The abstraction delegates most of the work to the implementation object that’s referenced in that field.

    6. If you have several variants of high-level logic, create refined abstractions for each variant by extending the base abstraction class.

    7. The client code should pass an implementation object to the abstraction’s constructor to associate one with the other. After that, the client can forget about the implementation and work only with the abstraction object.

  • Pros

    • You can create platform-independent classes and apps.

    • The client code works with high-level abstractions. It isn’t exposed to the platform details.

    • Open/Closed Principle. You can introduce new abstractions and implementations independently from each other.

    • Single Responsibility Principle. You can focus on high-level logic in the abstraction and on platform details in the implementation.

  • Cons

    • You might make the code more complicated by applying the pattern to a highly cohesive class.

  • Relation with other patterns

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

    • Bridgearrow-up-right, Statearrow-up-right, Strategyarrow-up-right (and to some degree Adapterarrow-up-right) 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.

    • You can use Abstract Factoryarrow-up-right along with Bridgearrow-up-right. 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.

    • You can combine Builderarrow-up-right with Bridgearrow-up-right: the director class plays the role of the abstraction, while different builders act as implementations.

  • Code

Composite

  • lets you compose objects into tree structures and then work with these structures as if they were individual objects.

  • Problem

    • imagine that you have two types of objects: Products and Boxes. A Box can contain several Products as well as a number of smaller Boxes. These little Boxes can also hold some Products or even smaller Boxes, and so on.

    • Say you decide to create an ordering system that uses these classes. Orders could contain simple products without any wrapping, as well as boxes stuffed with products...and other boxes. How would you determine the total price of such an order?

    • You could try the direct approach: unwrap all the boxes, go over all the products and then calculate the total.

    • That would be doable in the real world; but in a program, it’s not as simple as running a loop.

    • You have to know the classes of Products and Boxes you’re going through, the nesting level of the boxes and other nasty details beforehand. All of this makes the direct approach either too awkward or even impossible.

  • Solution

    • The Composite pattern suggests that you work with Products and Boxes through a common interface which declares a method for calculating the total price.

    • How would this method work? For a product, it’d simply return the product’s price.

    • For a box, it’d go over each item the box contains, ask its price and then return a total for this box.

    • If one of these items were a smaller box, that box would also start going over its contents and so on, until the prices of all inner components were calculated.

    • A box could even add some extra cost to the final price, such as packaging cost.

    • The greatest benefit of this approach is that you don’t need to care about the concrete classes of objects that compose the tree.

    • You don’t need to know whether an object is a simple product or a sophisticated box. You can treat them all the same via the common interface.

    • When you call a method, the objects themselves pass the request down the tree.

  • Real world analogy

    • Armies of most countries are structured as hierarchies. An army consists of several divisions; a division is a set of brigades, and a brigade consists of platoons, which can be broken down into squads.

    • Finally, a squad is a small group of real soldiers. Orders are given at the top of the hierarchy and passed down onto each level until every soldier knows what needs to be done.

  • Structure

    1. The Component interface describes operations that are common to both simple and complex elements of the tree.

    2. The Leaf is a basic element of a tree that doesn’t have sub-elements.

      Usually, leaf components end up doing most of the real work, since they don’t have anyone to delegate the work to.

    3. The Container (aka composite) is an element that has sub-elements: leaves or other containers. A container doesn’t know the concrete classes of its children. It works with all sub-elements only via the component interface.

      Upon receiving a request, a container delegates the work to its sub-elements, processes intermediate results and then returns the final result to the client.

    4. The Client works with all elements through the component interface. As a result, the client can work in the same way with both simple or complex elements of the tree.

  • Applicability

    • Use the Composite pattern when you have to implement a tree-like object structure.

    • The Composite pattern provides you with two basic element types that share a common interface: simple leaves and complex containers. A container can be composed of both leaves and other containers. This lets you construct a nested recursive object structure that resembles a tree.

    • Use the pattern when you want the client code to treat both simple and complex elements uniformly.

    • All elements defined by the Composite pattern share a common interface. Using this interface, the client doesn’t have to worry about the concrete class of the objects it works with.

  • How to implement

    1. Make sure that the core model of your app can be represented as a tree structure. Try to break it down into simple elements and containers. Remember that containers must be able to contain both simple elements and other containers.

    2. Declare the component interface with a list of methods that make sense for both simple and complex components.

    3. Create a leaf class to represent simple elements. A program may have multiple different leaf classes.

    4. Create a container class to represent complex elements. In this class, provide an array field for storing references to sub-elements. The array must be able to store both leaves and containers, so make sure it’s declared with the component interface type.

      While implementing the methods of the component interface, remember that a container is supposed to be delegating most of the work to sub-elements.

    5. Finally, define the methods for adding and removal of child elements in the container.

      Keep in mind that these operations can be declared in the component interface. This would violate the Interface Segregation Principle because the methods will be empty in the leaf class. However, the client will be able to treat all the elements equally, even when composing the tree.

  • Pros

    • You can work with complex tree structures more conveniently: use polymorphism and recursion to your advantage.

    • Open/Closed Principle. You can introduce new element types into the app without breaking the existing code, which now works with the object tree.

  • Cons

    • It might be difficult to provide a common interface for classes whose functionality differs too much.

    • In certain scenarios, you’d need to over generalize the component interface, making it harder to comprehend.

  • Relations with other patterns

  • Code

Decorator

  • lets you attach new behaviors to objects by placing these objects inside special wrapper objects that contain the behaviors.

  • Problem

    • Imagine that you’re working on a notification library which lets other programs notify their users about important events.

    • The initial version of the library was based on the Notifier class that had only a few fields, a constructor and a single send method.

    • The method could accept a message argument from a client and send the message to a list of emails that were passed to the notifier via its constructor.

    • A third-party app which acted as a client was supposed to create and configure the notifier object once, and then use it each time something important happened.

    • At some point, you realize that users of the library expect more than just email notifications.

    • Many of them would like to receive an SMS about critical issues. Others would like to be notified on Facebook and, of course, the corporate users would love to get Slack notifications.

    • How hard can that be? You extended the Notifier class and put the additional notification methods into new subclasses. Now the client was supposed to instantiate the desired notification class and use it for all further notifications.

    • But then someone reasonably asked you, “Why can’t you use several notification types at once? If your house is on fire, you’d probably want to be informed through every channel.”

    • You tried to address that problem by creating special subclasses which combined several notification methods within one class. However, it quickly became apparent that this approach would bloat the code immensely, not only the library code but the client code as well.

    • You have to find some other way to structure notifications classes so that their number won’t accidentally break some Guinness record.

  • Solution

    • Extending a class is the first thing that comes to mind when you need to alter an object’s behavior. However, inheritance has several serious caveats that you need to be aware of.

      • Inheritance is static. You can’t alter the behavior of an existing object at runtime. You can only replace the whole object with another one that’s created from a different subclass.

      • Subclasses can have just one parent class. In most languages, inheritance doesn’t let a class inherit behaviors of multiple classes at the same time.

    • With this new approach you can easily substitute the linked “helper” object with another, changing the behavior of the container at runtime. An object can use the behavior of various classes, having references to multiple objects and delegating them all kinds of work. Aggregation/composition is the key principle behind many design patterns, including Decorator. On that note, let’s return to the pattern discussion.

    • One of the ways to overcome these caveats is by using Aggregation or Composition info-circle instead of Inheritance. Both of the alternatives work almost the same way: one object has a reference to another and delegates it some work, whereas with inheritance, the object itself is able to do that work, inheriting the behavior from its superclass.

    • Wrapper” is the alternative nickname for the Decorator pattern that clearly expresses the main idea of the pattern. A wrapper is an object that can be linked with some target object. 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.

    • When does a simple wrapper become the real decorator? As I mentioned, the wrapper implements the same interface as the wrapped object. That’s why from the client’s perspective these objects are identical. Make the wrapper’s reference field accept any object that follows that interface. This will let you cover an object in multiple wrappers, adding the combined behavior of all the wrappers to it.

    • The client code would need to wrap a basic notifier object into a set of decorators that match the client’s preferences. The resulting objects will be structured as a stack.

    • The last decorator in the stack would be the object that the client actually works with. Since all decorators implement the same interface as the base notifier, the rest of the client code won’t care whether it works with the “pure” notifier object or the decorated one.

  • Real world analogy

    • Wearing clothes is an example of using decorators. When you’re cold, you wrap yourself in a sweater.

    • If you’re still cold with a sweater, you can wear a jacket on top. If it’s raining, you can put on a raincoat.

    • All of these garments “extend” your basic behavior but aren’t part of you, and you can easily take off any piece of clothing whenever you don’t need it.

  • Structure

      1. The Component declares the common interface for both wrappers and wrapped objects.

      2. Concrete Component is a class of objects being wrapped. It defines the basic behavior, which can be altered by decorators.

      3. The Base Decorator class has a field for referencing a wrapped object. The field’s type should be declared as the component interface so it can contain both concrete components and decorators. The base decorator delegates all operations to the wrapped object.

      4. Concrete Decorators define extra behaviors that can be added to components dynamically. Concrete decorators override methods of the base decorator and execute their behavior either before or after calling the parent method.

      5. The Client can wrap components in multiple layers of decorators, as long as it works with all objects via the component interface.

  • Applicability

    • Use the Decorator pattern when you need to be able to assign extra behaviors to objects at runtime without breaking the code that uses these objects.

    • The Decorator lets you structure your business logic into layers, create a decorator for each layer and compose objects with various combinations of this logic at runtime. The client code can treat all these objects in the same way, since they all follow a common interface.

    • Use the pattern when it’s awkward or not possible to extend an object’s behavior using inheritance.

    • Many programming languages have the final keyword that can be used to prevent further extension of a class. For a final class, the only way to reuse the existing behavior would be to wrap the class with your own wrapper, using the Decorator pattern.

  • How to implement

    1. Make sure your business domain can be represented as a primary component with multiple optional layers over it.

    2. Figure out what methods are common to both the primary component and the optional layers. Create a component interface and declare those methods there.

    3. Create a concrete component class and define the base behavior in it.

    4. Create a base decorator class. It should have a field for storing a reference to a wrapped object. The field should be declared with the component interface type to allow linking to concrete components as well as decorators. The base decorator must delegate all work to the wrapped object.

    5. Make sure all classes implement the component interface.

    6. Create concrete decorators by extending them from the base decorator. A concrete decorator must execute its behavior before or after the call to the parent method (which always delegates to the wrapped object).

    7. The client code must be responsible for creating decorators and composing them in the way the client needs.

  • Pros

    • You can extend an object’s behavior without making a new subclass.

    • You can add or remove responsibilities from an object at runtime.

    • You can combine several behaviors by wrapping an object into multiple decorators.

    • Single Responsibility Principle. You can divide a monolithic class that implements many possible variants of behavior into several smaller classes.

  • Cons

    • It’s hard to remove a specific wrapper from the wrappers stack.

    • fw It’s hard to implement a decorator in such a way that its behavior doesn’t depend on the order in the decorators stack.

    • fw The initial configuration code of layers might look pretty ugly.

  • Relations with other patterns

    • Adapterarrow-up-right provides a completely different interface for accessing an existing object. On the other hand, with the Decoratorarrow-up-right pattern the interface either stays the same or gets extended. In addition, Decorator supports recursive composition, which isn’t possible when you use Adapter.

    • With Adapterarrow-up-right you access an existing object via different interface. With Proxyarrow-up-right, the interface stays the same. With Decoratorarrow-up-right you access the object via an enhanced interface.

    • Chain of Responsibilityarrow-up-right and Decoratorarrow-up-right 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.

    • Compositearrow-up-right and Decoratorarrow-up-right 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.

    • Designs that make heavy use of Compositearrow-up-right and Decoratorarrow-up-right can often benefit from using Prototypearrow-up-right. Applying the pattern lets you clone complex structures instead of re-constructing them from scratch.

    • Decoratorarrow-up-right lets you change the skin of an object, while Strategyarrow-up-right lets you change the guts.

    • Decoratorarrow-up-right and Proxyarrow-up-right 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.

  • Code

Flywheel/ Flyweight

  • Structural pattern that lets us fit more objects into the available amount of RAM by sharing common parts of the state between multiple objects instead of keeping all of the data in each object.

  • Problem

    • lets suppose we built a game which is working fine in our system but due to ram constraints it is not working properly in other systems

    • The problem that we found out that each particle in the game was represented by a separate object containing plenty of data.

    • At some point the carnage on a player screen reached its climax, newly created particles no longer fit into the ram crashing the program.

  • Solution

    • On closer inspection we found out that the color and sprite fields consume a lot more memory than other fields, the worse part is that these two fields store almost identical data across all particles.

    • other parts of a particle state such as coordinates, movement vector and speed are unique to each particle.

    • After all the values of these fields change over time, the data represents the always changing context in which the particle exists, while the color and sprite remain constant for each particle

    • The Flyweight patterns suggests that we stop storing the extrensic state inside the object, instead one could pass this state to specific methods which rely on it

    • Only the intrinsic state stays within the object, letting us reuse it in different contexts. As a result we would need fewer of thest objects since they only differ in the intrinsic state which has much fewer variations than the extrinsic.

    • Extrinsic state storage

      • Where does the extrinsic state move to, some class should still store it right, in most cases it gets moved to the container object, which aggregates objects before we apply the pattern.

      • in our case that the main game object that stores all particles in the particles field, to move the extrisnic state into this class, we need to create several array fields for storing references to a specific flyweight that represents a particle

      • These arrays must be in sync so that one can access all data of a particle using the same index.

      • A more elegant solution is to create a seperate context class that would store the extrinsic state along with reference to the flyweight object.

      • This approach would require having just a single array in the container class

      • Wait a second! Won’t we need to have as many of these contextual objects as we had at the very beginning? Technically, yes. But the thing is, these objects are much smaller than before.

      • The most memory-consuming fields have been moved to just a few flyweight objects. Now, a thousand small contextual objects can reuse a single heavy flyweight object instead of storing a thousand copies of its data.

    • Flyweight and immutability

      • since the same flywegiht object can be used in different contexts one have to make sure that its state can't be modified.

      • A flywegiht shoudl initialize its state just one via a constructor parameter, it shouldn't expose any setters or publuc fields to other object

    • Flyweight factory

      • For more convenient access to various flyweights, you can create a factory method that manages a pool of existing flyweight objects.

      • The method accepts the intrinsic state of the desired flyweight from a client, looks for an existing flyweight object matching this state, and returns it if it was found. If not, it creates a new flyweight and adds it to the pool.

      • There are several options where this method could be placed. The most obvious place is a flyweight container. Alternatively, you could create a new factory class. Or you could make the factory method static and put it inside an actual flyweight class.

  • Structure

    • The Flyweight pattern is merely an optimization. Before applying it, make sure your program does have the RAM consumption problem related to having a massive number of similar objects in memory at the same time. Make sure that this problem can’t be solved in any other meaningful way.

    • The Flyweight class contains the portion of the original object’s state that can be shared between multiple objects. The same flyweight object can be used in many different contexts. The state stored inside a flyweight is called intrinsic. The state passed to the flyweight’s methods is called extrinsic.

    • The Context class contains the extrinsic state, unique across all original objects. When a context is paired with one of the flyweight objects, it represents the full state of the original object.

    • Usually, the behavior of the original object remains in the flyweight class. In this case, whoever calls a flyweight’s method must also pass appropriate bits of the extrinsic state into the method’s parameters. On the other hand, the behavior can be moved to the context class, which would use the linked flyweight merely as a data object.

    • The Client calculates or stores the extrinsic state of flyweights. From the client’s perspective, a flyweight is a template object which can be configured at runtime by passing some contextual data into parameters of its methods.

    • The Flyweight Factory manages a pool of existing flyweights. With the factory, clients don’t create flyweights directly. Instead, they call the factory, passing it bits of the intrinsic state of the desired flyweight. The factory looks over previously created flyweights and either returns an existing one that matches search criteria or creates a new one if nothing is found.

  • Applicability

    • Use the Flyweight pattern only when your program must support a huge number of objects which barely fit into available RAM.

    • The benefit of applying the pattern depends heavily on how and where it’s used. It’s most useful when:

      • an application needs to spawn a huge number of similar objects

      • this drains all available RAM on a target device

      • the objects contain duplicate states which can be extracted and shared between multiple objects

  • How to implement

    1. Divide fields of a class that will become a flyweight into two parts:

      • the intrinsic state: the fields that contain unchanging data duplicated across many objects

      • the extrinsic state: the fields that contain contextual data unique to each object

    2. Leave the fields that represent the intrinsic state in the class, but make sure they’re immutable. They should take their initial values only inside the constructor.

    3. Go over methods that use fields of the extrinsic state. For each field used in the method, introduce a new parameter and use it instead of the field.

    4. Optionally, create a factory class to manage the pool of flyweights. It should check for an existing flyweight before creating a new one. Once the factory is in place, clients must only request flyweights through it. They should describe the desired flyweight by passing its intrinsic state to the factory.

    5. The client must store or calculate values of the extrinsic state (context) to be able to call methods of flyweight objects. For the sake of convenience, the extrinsic state along with the flyweight-referencing field may be moved to a separate context class.

  • Pros

    • You can save lots of RAM, assuming your program has tons of similar objects.

  • Cons

    • You might be trading RAM over CPU cycles when some of the context data needs to be recalculated each time somebody calls a flyweight method.

    • The code becomes much more complicated. New team members will always be wondering why the state of an entity was separated in such a way.

    fw

  • Relations with other patterns

    • You can implement shared leaf nodes of the Compositearrow-up-right tree as Flyweightsarrow-up-right to save some RAM.

    • Flyweightarrow-up-right shows how to make lots of little objects, whereas Facadearrow-up-right shows how to make a single object that represents an entire subsystem.

    • Flyweightarrow-up-right would resemble Singletonarrow-up-right 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:

      1. There should be only one Singleton instance, whereas a Flyweight class can have multiple instances with different intrinsic states.

      2. The Singleton object can be mutable. Flyweight objects are immutable.

  • Code

Facade

  • structural design pattern that provides a simplified interface to a library, a framework, or any other complex set of classes.

  • Problem

    • Imagine that you must make your code work with a broad set of objects that belong to a sophisticated library or framework. Ordinarily, you’d need to initialize all of those objects, keep track of dependencies, execute methods in the correct order, and so on.

    • As a result, the business logic of your classes would become tightly coupled to the implementation details of 3rd-party classes, making it hard to comprehend and maintain.

  • Solution

    • A facade is a class that provides a simple interface to a complex subsystem which contains lots of moving parts. A facade might provide limited functionality in comparison to working with the subsystem directly.

    • However, it includes only those features that clients really care about.

    • Having a facade is handy when you need to integrate your app with a sophisticated library that has dozens of features, but you just need a tiny bit of its functionality.

    • For instance, an app that uploads short funny videos with cats to social media could potentially use a professional video conversion library.

    • However, all that it really needs is a class with the single method encode(filename, format). After creating such a class and connecting it with the video conversion library, you’ll have your first facade.

  • Real world analogy

    • When you call a shop to place a phone order, an operator is your facade to all services and departments of the shop.

    • The operator provides you with a simple voice interface to the ordering system, payment gateways, and various delivery services.

  • Structure

    1. The Facade provides convenient access to a particular part of the subsystem’s functionality. It knows where to direct the client’s request and how to operate all the moving parts.

    2. An Additional Facade class can be created to prevent polluting a single facade with unrelated features that might make it yet another complex structure. Additional facades can be used by both clients and other facades.

    3. The Complex Subsystem consists of dozens of various objects. To make them all do something meaningful, you have to dive deep into the subsystem’s implementation details, such as initialising objects in the correct order and supplying them with data in the proper format.

      Subsystem classes aren’t aware of the facade’s existence. They operate within the system and work with each other directly.

    4. The Client uses the facade instead of calling the subsystem objects directly.

  • Applicability

    • Use the Facade pattern when you need to have a limited but straightforward interface to a complex subsystem.

    • Often, subsystems get more complex over time. Even applying design patterns typically leads to creating more classes. A

    • subsystem may become more flexible and easier to reuse in various contexts, but the amount of configuration and boilerplate code it demands from a client grows ever larger. The Facade attempts to fix this problem by providing a shortcut to the most-used features of the subsystem which fit most client requirements.

    • Use the Facade when you want to structure a subsystem into layers.

    • Create facades to define entry points to each level of a subsystem. You can reduce coupling between multiple subsystems by requiring them to communicate only through facades.

    • For example, let’s return to our video conversion framework. It can be broken down into two layers: video- and audio-related. For each layer, you can create a facade and then make the classes of each layer communicate with each other via those facades. This approach looks very similar to the Mediatorarrow-up-right pattern.

  • How to implement

    1. Check whether it’s possible to provide a simpler interface than what an existing subsystem already provides. You’re on the right track if this interface makes the client code independent from many of the subsystem’s classes.

    2. Declare and implement this interface in a new facade class. The facade should redirect the calls from the client code to appropriate objects of the subsystem. The facade should be responsible for initializing the subsystem and managing its further life cycle unless the client code already does this.

    3. To get the full benefit from the pattern, make all the client code communicate with the subsystem only via the facade. Now the client code is protected from any changes in the subsystem code. For example, when a subsystem gets upgraded to a new version, you will only need to modify the code in the facade.

    4. If the facade becomes too bigarrow-up-right, consider extracting part of its behavior to a new, refined facade class.

  • Pros

    • You can isolate your code from the complexity of a subsystem.

  • fwCons

  • Relations with other patterns

    • Facadearrow-up-right defines a new interface for existing objects, whereas Adapterarrow-up-right tries to make the existing interface usable. Adapter usually wraps just one object, while Facade works with an entire subsystem of objects.

    • Abstract Factoryarrow-up-right can serve as an alternative to Facadearrow-up-right when you only want to hide the way the subsystem objects are created from the client code.

    • Flyweightarrow-up-right shows how to make lots of little objects, whereas Facadearrow-up-right shows how to make a single object that represents an entire subsystem.

    • Facadearrow-up-right and Mediatorarrow-up-right 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.

    • A Facadearrow-up-right class can often be transformed into a Singletonarrow-up-right since a single facade object is sufficient in most cases.

    • Facadearrow-up-right is similar to Proxyarrow-up-right 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.

  • Code

Proxy

  • provide a substitute or placeholder for another object.

  • A proxy controls access to the original object, allowing you to perform something either before or after the request gets through to the original object.

  • Problem

    • Why would you want to control access to an object?

      • you have a massive object that consumes a vast amount of system resources. You need it from time to time, but not always.

      • You could implement lazy initialization: create this object only when it’s actually needed. All of the object’s clients would need to execute some deferred initialisation code. Unfortunately, this would probably cause a lot of code duplication.

      • In an ideal world, we’d want to put this code directly into our object’s class, but that isn’t always possible. For instance, the class may be part of a closed 3rd-party library.

  • SOlution

    • Proxy pattern suggests that you create a new proxy class with the same interface as an original service object.

    • Then you update your app so that it passes the proxy object to all of the original object’s clients.

    • Upon receiving a request from a client, the proxy creates a real service object and delegates all the work to it.

Last updated