Creational Patterns are concerned with the style, approach and rules of creating new objects.
Acronym to remember: “ABFPS”
[A]bstract Factory, [B]uilder, [F]actory Method, [P]rototype, [S]ingleton
>> “Abstract Factory”
“Provide an interface for creating families of related or dependent objects without specifying their concrete classes. Clients are not aware of the concrete classes, just aware of the Factory interface.”
This DP involves writing up a class structure where the objects to be created are decided at runtime. The (Abstract) Factory superclass (GUIFactory) has methods to create objects, and those methods are overriden in (Concrete) Factory subclasses (SwingFactory, JavaFXFactory …). If a client object invokes one of these subclass creation methods, it returns subclass objects (SwingScrollbar, JavaFXScrollbar, …), that inherit from an (Abstract) superclass (Scrollbar).
Consequences of using Abstract Factory:
- Class Implementations are isolated from the client. The client only uses interface type variables that point to the implementation class that is returned by the AF. It has no concrete class references.
- Families of product can be changed easily. The client can invoke a different factory method to get a different implementation with little effort.
- The implementation can change and the client will be unaffected. The logic in the factory can change or be updated and the client is unaffected, since the client only deals with the interface variable and since construction details are encapsulated in the factory.
“Separate the construction of a complex object from it’s representation so that the same construction process can create different representations. An object doesn’t populate itself, but instead an external object handles that.”
This DP has a class (TextReaderClient) create an object (Result) using an external “builder” class (ConverterBuilder). The advantage of doing this is that the final object created can vary programmatically, at run time. The client (TextReaderClient) does this by either using an instantiated object (ConverterBuilder) as one of its fields, or the builder is passed into the client via the build method (parseText()). Either way, the client then invokes each of the “builder” (ConverterBuilder) methods in order, collecting the results, and finally assembling them to return a finished object.
Consequences of using Builder:
- The internal logic of building an object is hidden from the client. Like other creational patterns, the builder does all the hard work behind the scenes.
- Different build method calls will result in a different end product. Mix and match of say different “buildNextPart()” calls could ideally result in a returned object that is configured differently, or result in a completely different object.
- Finer control over the “instantiate object” process. As for point #2, but also just that other creational patterns have a single method call that returns an object, whereas Builder allows the client to control additional steps in creation of the object.
>> “Factory Method”
“Define an interface (in the client object) for creating a (new) object, but let (client) subclass decide which (new object) to instantiate. Defer instantiation to the (client) subclass.”
This DP is very similar to “Abstract Factory”. The difference is that in AF, new objects can be generated when the designer creates multiple new subclasses (SwingFactory, JavaFXFactory) for use by the Client. Whereas in FM, the designer subclasses the Application as a single class, “MyApplication”, and it has the only creation method (createDocument()). The Factory and the Client are in the same class (MyApplication). This is best for low-complexity implementations that aren’t expected to vary at runtime, because MyApplicationClient doesn’t vary at runtime.
Consequences of using Factory Method:
- You must subclass a parent class to override an object creation method. The parent can have an abstract method that must be overriden or a default implemented method that is overriden when the default method is insufficient.
- The class hierarchy of the class containing the factory method may mirror the class hierarchy of the objects to be instantiated. In other words, you could have the same number of classes that contain factory methods as there are objects to be created.
- You can use parameters passed in to the factory method to change the product returned. Going one step further, then if you subclass the application, you could have a different set of parameters that control object instantiation than in the parent class.
“Specify the kinds of objects to create, using a prototypical instance, and create new objects by copying or cloning this prototype.”
The idea is that an instance of the client class (GraphicTool) invokes a method on one of its nested objects, i.e.: circle1.clone(), that clones object circle1 and returns a reference to a new cloned object circle2. circle2 is a copy of circle1. Now the instance of GraphicTool has a reference to both circle1 and circle2 and can use both of them accordingly. This is nice because the data from circle1 is copied over to circle2 by circle1 itself, avoiding having that “copy” logic in the client, and encapsulating that logic in circle1 where it logically most belongs.
The downside to the prototype DP is that although shallow copies are straightforward, deep copies are less so, especially if the prototype object has nested circular references or if a nested field of the prototype is a class that doesn’t itself support cloning. The coder of a prototype class must decide between: a) making full copies of nested fields, or b) copying only the references to nested fields so that all copies point to the same global/static fields. “a)” is preferred but not always possible.
Consequences of using Prototype:
- Hides the cloning logic in the clones. Client deals with an interface reference, and the clone encapsulates the implementation logic.
- Adding and removing products (cloned objects) at runtime. It the client code starts out with a single prototype, over the course of the program execution it can create or remove copies at will.
- Specify new objects by varying their values. Instead of having to define new top-level/encapsulating classes when redesigning or expanding a system, the coder can change existing nested prototype classes to accept different data and thereby exhibit different functionality. Object “composition” is then favored over class “inheritance” with this approach.
- Specify new objects by varying structure. Prototype objects fulfill the goal of code reuse.
- Reduced subclassing. In AF and FM DPs, the “creator” class hierarchy mirrors the “product” class hierarchy. In the Prototype DP, the “creator” and “product” class hierarchies are one in the same.
“Ensure that a class has only one instance, and provide a global point of access to it.”
This DP is used when the client should only ever have access to either a) a limited number of instances collected into a “pool”, or b) a single instance of a certain object. For instance, for option “a”, a web server may have a connection pool containing a limited number of HTTP connection objects that are reused for new users instead of garbage collected. For true singletons, as in the diagram above, when the client code invokes the static method “newInstance()”, the Company object first checks if the field “theCompany” (the only instantiated instance of the Company class) has been instantiated yet. If not, “theCompany” object is instantiated using the private constructor (which only it has access to), and then for either condition the single reference to “theCompany” object is passed back to the client for use.
Consequences of using Singleton:
- Controlled access to sole instance. Singleton class has strict control over how and when a client has access to the sole instance.
- Reduced global variables. Singletons can be used as a replacement for global variables in a class. Global variables, while handy for use internal to a class since they can be used across multiple methods, can be tricky to use as the number of methods in a class goes up.
- Extended class to get new functionality or restrictions. The singleton class can be extended so that different singletons can be selected for use at runtime, where functionality or the restrictions on how they are instantiated can vary.
- An alternate version of Singleton is to have variable number of instances available. Refactoring the code to make Singletons loosen the single access restriction is straightforward if desired at some point.
- More flexible than using a static class. Static methods in Java can’t be inherited, because they are not swapped out at runtime as is done for non-static overriding. On the other hand, methods in a Singleton class can be inherited in subclass versions, which is desirable to gain new functionality or restrictions.
Note: Much of the information used here comes from “Design Patterns: Elements of Reusable Object-Oriented Software” by Erich Gamma, Richard Helm, Ralph Johnson and John Vlissides.