Creational design patterns Flashcards
Factory Method
Factory Method is a creational design pattern that provides an interface for creating objects in a superclass, but allows subclasses to alter the type of objects that will be created.
The 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.
There’s a slight limitation though: subclasses may return different types of products only if these products have a common base class or interface. Also, the factory method in the base class should have its return type declared as this interface.
Use the Factory Method when you want to provide users of your library or framework with a way to extend its internal components.
Can substitute inheritance
Use the Factory Method when you want to save system resources by reusing existing objects instead of rebuilding them each time.
How to Implement factory method
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.
For instance, imagine that you have the following hierarchy of classes: the base Mail class with a couple of subclasses: AirMail and GroundMail; the Transport classes are Plane, Truck and Train. While the AirMail class only uses Plane objects, GroundMail may work with both Truck and Train objects. You can create a new subclass (say TrainMail) to handle both cases, but there’s another option. The client code can pass an argument to the factory method of the GroundMail class to control which product it wants to receive.
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.
Factory 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.
Factory 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.
Factory Relations with Other Patterns
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.
Abstract Factory
is a creational design pattern that lets you produce families of related objects without specifying their concrete classes.
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 Methods 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.
Pros Factory method
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.
Factory 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.
Abstract Factory relationships
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).
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.
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.
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.
Abstract Factories, Builders and Prototypes can all be implemented as Singletons.
Builder method
is a creational design pattern that lets you construct complex objects step by step. The pattern allows you to produce different types and representations of an object using the same construction code.
Builder solution
The 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.
Design pattern
A standard solution to a common
software problem in a context.
* describes a recurring software structure or idiom
* is abstract from any particular programming language
* identifies classes and their roles in the solution to a
problem
In general, a pattern has four essential elements.
The pattern name
▪ The problem
▪ The solution
▪ The consequences
The Pattern Name
The pattern name is a handle we can use to describe a
design problem, its solutions, and consequences in a
word or two.
▪ Naming a pattern immediately increases the design
vocabulary. It lets us design at a higher level of
abstraction.
▪ Having a vocabulary for patterns lets us talk about
them.
▪ It makes it easier to think about designs and to
communicate them and their trade-offs to others.
The problem
describes when to apply the pattern.
▪ It explains the problem and its context.
▪ It might describe specific design problems such as how to
represent algorithms as objects.
▪ It might describe class or object structures that are
symptomatic of an inflexible design.
▪ Sometimes the problem will include a list of conditions that
must be met before it makes sense to apply the pattern.
The solution
describes the elements that make up the design, their
relationships, responsibilities, and collaborations.
▪ The solution doesn’t describe a particular concrete design or
implementation, because a pattern is like a template that can be
applied in many different situations.
▪ Instead, the pattern provides an abstract description of a design
problem and how a general arrangement of elements (classes
and objects in our case) solves it
The consequences
are the results and trade-offs of applying the
pattern.
▪ The consequences for software often concern space and time
trade-offs.
▪ They may address language and implementation issues as well.
▪ Since reuse is often a factor in object-oriented design, the
consequences of a pattern include its impact on a system’s
flexibility, extensibility, or portability.
▪ Listing these consequences explicitly helps you understand and
evaluate them.