4.2 Creational Design Patterns
Creational Design Patterns are concerned with object creation. They abstract the process of instantiating an object, thus decoupling the system from the mechanisms of creating objects. The creational patterns primarily make use of inheritance to provide the abstraction between the client code requesting the new object, and the specific concrete object which is created.
As the creational design patterns are all "competitors" - in that they are all used to solve the problem of instantiating an object - it is important to understand each of them well, to be able to select the most appropriate one for the specific situation being addressed.
The Singleton design pattern is used to ensure that only a single instance of a specific class is instantiated.
Many scenarios exist where it is useful to only have a single instance of an object. For example, if you have an Application object, which holds all the various components of the application, or a Database object, which has the connection to the database server and is used to execute SQL queries, or a Configuration object which holds the configuration for an application. In these examples, it would not make sense to have multiple instantiations of each of these classes. Thus, we would want an existing instance of an object to be returned if we tried to instantiate any of these classes.
The solution to this problem is to define an attribute to hold the single instance of the object. If this instance attribute is null, then a new instance is assigned and returned, otherwise the instance contained in the attribute is returned.
There are a number of ways we can implement a singleton in Python. One convenient way is to define a Singleton class which can be inherited from into other classes in which the Singleton design pattern is to be applied. Here is an example of such an inheritable class.
class Singleton(type): def __init__(cls, name, bases, dict): super(Singleton, cls).__init__(name, bases, dict) cls.instance = None def __call__(cls,*args,**kw): if cls.instance is None: cls.instance = super(Singleton, cls).__call__(*args, **kw) return cls.instance
To make use of this, we specify the __metaclass__ of our class implementing the singleton design patter. Here is an example.
class MyClass(object): __metaclass__ = Singleton attrib1 = "" attrib2 = 0 def function1(self): pass def function2(self): pass
Now, to test if the Singleton pattern has worked, we can create two instances of the MyClass class which implements the singleton pattern, and each of the instances should point to the same memory location, indicating that they are all referring to the same instance of the class.
if __name__ == '__main__': # Instantiate Object A objA = MyClass() # Instantiate Object B objB = MyClass() # Investigate the memory addresses of the two objects print "Object A = %d" % (id(objA)) print "Object B = %d" % (id(objB)) # Determine if they are the same instance if (id(objA) == id(objB)): print "Same" else: print "Different"
When you run this code, you should see that both memory addresses are equivalent, and "Same" is printed out as a result, indicating that the Singleton design pattern has been successfully implemented.
The Factory Method Pattern is intended to be used when we want subclasses to choose which classes they should instantiate when an object is requested. The Factory design pattern abstracts the object instantiation process by allowing subclasses to decide which class to instantiate. The factory pattern introduces a separation between the application and a family of classes, resulting in loose coupling between the concrete child classes and the application.
The Factory design pattern is used in the situation where a class does not know the specific child class to instantiate, or where the child class to instantiate is dependant on the instantiation of another child class.
For example, imagine we are writing an application for a real estate agency. They might have the need to produce various documents (such as lease agreements) depending on the type of property involved. Thus, we might have a Property abstract class, from which the various child classes - representing property types - inherit. Then we might also have a Document abstract class, from which the various child classes - representing specific document templates - inherits. However, the Property parent class will not know exactly which of the specific document sub classes to create. Thus, the instantiation logic needs to reside in the child classes.
In the Factory design pattern we have a number of "participants" (or classes) defined:
- The Factory: The parent class from which the client code is requesting the concrete product. In our example above, this would be the Property abstract class.
- Concrete Factory: The concrete instance of the abstract Factory class. In our above example, this would be a specific property type.
- Product: This is the abstract class which provides the common interface for the concrete products. In our above example, this would be the Document abstract class.
- Concrete Product: This is the concrete child class which inherits from the parent Product class, and is created by the Concrete Factory class.
Using our example (see above) of the Real Estate application, let's look at how we would implement this using the Factory Design Pattern in Python.
class Property: def DocumentFactory(self): return self.ConcreteDocumentFactory() class House(Property): def ConcreteDocumentFactory(self): return HouseRentalDocument() class Flat(Property): def ConcreteDocumentFactory(self): return FlatRentalDocument() class Document: title = "Lease Agreement" class HouseRentalDocument(Document): def __init__(self): self.title = "House Lease Agreement" class FlatRentalDocument(Document): def __init__(self): self.title = "Apartment Lease Agreement" if __name__ == '__main__': # Test: House house = House() doc = house.DocumentFactory() print "House Document Title: %s" % (doc.title) # Test: Flat flat = Flat() doc = flat.DocumentFactory() print "Flat Document Title: %s" % (doc.title)
In the above code, although we are calling the DocumentFactory() factory method from the Factory class, the responsibility for instantiation is in reality being delegated to the child classes.
The builder pattern is used to separate the logic for creating complex objects from the logic that constructs the various parts of the object. The builder patterns allows flexibility in construction of the object by allowing for different versions of the complex object to be built.
When we have the need to build a complex object - such as an email with various attachments, or a web form, with various input types - a flexible creational pattern needs to be implemented to allow for an object to be created using a common interface,
The Builder design pattern has the following components:
- Builder: A common interface for building parts of a Product.
- Concrete Builder: Children of the abstract Builder class, which build and assemble the various components of the Product.
- Director: Utilizes the Builder to construct a Product.
- Product: The complex object being built. This is a composite class, containing other classes which are the components being built into the instance of the Product object.
The client code will create a Director object, which then constructs the various components of the Product, which is then accessed by the client code.
Here is the class diagram of the Builder design pattern.
Let's take a look at an example in Python. In our example, we are going to apply the Builder design pattern to create a Form builder class. This will be able to build either a static form for printing, or a web form. We will be able to construct a product, the Form HTML, with varying input components. Let's take a look at the code:
from abc import ABCMeta, abstractmethod class AbstractFormBuilder: __metaclass__ = ABCMeta product = 0 def __init__(self): self.product = Product() @staticmethod def Factory(type): if type == "web": return WebFormBuilder() elif type == "static": return StaticFormBuilder() def add(self, type, label, name, default_value): self.concrete_add(type, label, name, default_value) @abstractmethod def getResult(self): pass class WebFormBuilder(AbstractFormBuilder): def concrete_add(self, type, label, name, default_value): if type == "text": self.add_text(label, name, default_value) elif type == "textarea": self.add_textarea(label, name, default_value) elif type == "select": self.add_select(label, name, default_value) def add_text(self, label, name, value): html_segment = "%s " % (label, name, value) self.product.add(html_segment) def add_textarea(self, label, name, value): html_segment = "%s %s" % (label, name, value) self.product.add(html_segment) def add_select(self, label, name, value): html_segment = "%s " % (label, name) options = value.split(',') for option in options: html_segment = html_segment + "%s" % (option, option) html_segment = html_segment + "" self.product.add(html_segment) def getResult(self): return "%s" % (self.product.get_html()) class StaticFormBuilder(AbstractFormBuilder): def concrete_add(self, type, label, name, default_value): if type == "text": self.add_text(label, name, default_value) elif type == "textarea": self.add_textarea(label, name, default_value) elif type == "select": self.add_select(label, name, default_value) def add_text(self, label, name, value): html_segment = "%s ________________________________" % (label) self.product.add(html_segment) def add_textarea(self, label, name, value): html_segment = "%s ________________________________________________________________________________________________" % (label) self.product.add(html_segment) def add_select(self, label, name, value): html_segment = "%s " % (label) options = value.split(',') for option in options: html_segment = html_segment + "_ %s " % (option) html_segment = html_segment + "" self.product.add(html_segment) def getResult(self): return self.product.get_html() class Form: builder = 0 def __init__(self, type): self.builder = AbstractFormBuilder.Factory(type) def add(self, type, label, name, value): self.builder.add(type, label, name, value) def generate(self): return self.builder.getResult() class Product: name = "" html = "" def add(self, html_segment): self.html = self.html + html_segment def get_html(self): return self.html if __name__ == '__main__': myForm = Form("web") myForm.add("text", "Username", "username", "") myForm.add("text", "Password", "password", "") myForm.add("select", "User Type", "user_type", "admin, employee, manager") print myForm.generate()
In the above coding example, we can identify the four classes involved in the Builder pattern:
- Builder: This is the AbstractFormBuilder class.
- Concrete Builder: These are the WebFormBuilder and StaticFormBuilder classes which inherit from the AbstractFormBuilder class.
- Director: This is the Form class, which directs the utilization of the other classes.
- Product: This contains the HTML for the form being built.
For the following examples, specify which design pattern is the most applicable, and describe how to use it to solve the specified problem.
- Storing the configuration for an application.
- Exporting data into multiple formats.
- Dynamically creating a survey.