Lecture recording (Tuesday 10 September, 2024) here.
Lab recording (Thursday 12 September, 2024) here.
We continue our study of creational patterns. The abstract factory provides an interface for creating families of related objects without specifying their concrete classes.The Builder pattern separates the construction of a complex object from its representation, allowing the same construction process to create different representations. The Prototype pattern creates new objects by copying an existing object, without coupling the code to the specific classes of those objects.
The Abstract Factory Pattern | Abstract Factory Design Pattern |
The Abstract Factory Pattern Explained and Implemented | |
The Builder Design Pattern | Builder Design Pattern Explained in 10 Minutes |
The Builder Pattern Explained and Implemented in Java | |
The Prototype Design Pattern | Prototype Design Pattern Tutorial |
The Prototype Pattern Explained and Implemented in Java |
Assignment 1 - The Retail Store
The Rationale
The Abstract Factory design pattern is used to provide an interface for creating related or dependent objects, without specifying their concrete classes. The main rationale behind the pattern is to provide a way to create families of related objects that can work together without specifying their concrete classes. The objects are created through a common interface or abstract class, which is implemented by a set of concrete classes. This allows the client code to create objects without knowing their concrete classes, and it ensures that the created objects are compatible with each other and can work together.
The UML
Here is the UML diagram for the abstract factory design pattern:
Here are the components of the abstract factory design pattern:
Code Example - Abstract Factory
The following is sample code for the abstract factory:
C++: AbstractFactory.cpp.
C#: Program.cs.
Java: AbstractMain.java.
Python: Python.py.
Common Usage
The following are some common usages of the abstract factory pattern:
Code Problem - GUI Application
We wish to write graphics code that can run on any machine. In this use-case scenario, the machines are Windows and Mac. The following program uses the abstract factory pattern in generating graphics for both windows and mac machines.
Widget.h, the abstract product,
Button.h, the concrete product,
Textbox.h, the concrete product,
Theme.h, the abstract theme,
LightTheme.h, the concrete theme,
DarkTheme.h, the concrete theme,
GUIFactory.h, the abstract gui factory,
WindowsFactory.h, the concrete windows factory ,
MacFactory.h, the concrete mac factory,
GUIMain.cpp, the main function.
The Rationale
The Builder software design pattern is a creational pattern that aims to separate the construction of complex objects from their representation, allowing the same construction process to create different representations. This is achieved through creating complex objects step by step while keeping the creation process flexible and modular. This makes the pattern useful in scenarios where different clients may need different representations of the same object, or where the object itself may have multiple representations depending on the context in which it is used. By breaking down the creation process into smaller, more manageable steps, the pattern makes it easier to test and debug the code, as well as to reuse and extend the creation process in the future.
The UML
Here is the UML diagram for the Builder pattern:
Here are the components of the Builder design pattern:
Code Example - Pizza Builder
In the following example the Pizza class represents the final object being created, and the PizzaBuilder abstract class
defines the interface for creating parts of the final object. The concrete builders are HawaiianPizzaBuilder and SpicyPizzaBuilder.
The Cook class is the director. The code is written in four languages:
C++: Builder.cpp.
C#: Builder.cs.
Java: Builder.java.
Python: Builder.py.
Common Usage
Some common uses of the Builder pattern in software industry include:
Code Problem - Computer Builder
We want to build one of two types of computer - a low end computer and a high end computer. The low end computer will have an Intel Core i5-750 cpu, 16Gbytes of RAM, two hard disks (500GB Seagate SATA HDD and 1 TB Samsung 980 HDD), a graphics card (EVGA - XR1 Pro Capture Card), and requires a 500W power supply. The high end computer will have an Intel Core i9-11900K cpu, 32 GBytes of RAM, two hard disks (1 TB NVMe SSD and 4 TB HDD), a graphics card (NVIDIA GeForce RTX 3090), and requires a 1000W power supply.
The user will choose which type to build. Since the type is not known at compile time, both the low and and high end computers will have to be derived from a common base class so they can be treated the same way. We need a computer system director to direct the build process. And we need the computer system itself.
Code that implements the computer builder can be found at:
ComputerSystemBuilder.h, an abstract base class,
LowEndComputerSystemBuilder.h, derived from ComputerSystemBuilder,
HighEndComputerSystemBuilder.h, derived from ComputerSystemBuilder,
ComputerSystemDirector.h, directs the build process,
ComputerSystem.h, the computer system,
ComputerBuilderMain.cpp, the main function.
The Rationale
The Prototype software design pattern is used to create new objects by cloning existing ones, rather than by creating new
objects from scratch. The primary rationale behind this pattern is to reduce the cost of object creation.
When your objects have dozens of fields and hundreds of possible configurations, cloning them might serve as an
alternative to subclassing. An analogy to cloning is cell division:
The UML
Here is the UML diagram for the Builder pattern:
Here are the components of the Builder design pattern:
Code Example - Writing Tool
In the following example, we have two concrete prototype classes, Pen and Pencil, which both implement the WritingTool interface. Each class has its own implementation of the write() method, which prints out a message indicating which writing tool is being used to write the text.
We first create a prototype object of a Pen, clone it to create a new Pen object, and use it to write some text.
We then create a prototype object of a Pencil, clone it to create a new Pencil object, and use it to write some text.
Sample code is as follows:
C++: WritingTool.cpp.
C#: WritingTool.cs.
Java: WritingToolMain.java.
Python: Python.py.
Common Usage
Some specific examples of the use of the Prototype pattern in software development include:
Code Problem - Shape Cache
We want to be able to pull up one or more of several predefined shapes without having to invoke a constructor at run-time. In graphics programs, there may not be time to construct a complex graphical image while the program is running. Rather, we need these predefined shapes to have already been created for us. The predefined shapes in this example will be Rectangle with dimensions 10x20, a Rectangle with dimensions 20x40, a Rectangle with dimensions 40x80, a Circle with radius 5, a Circle with radius 10, and a Circle with radius 20.
Code that implements the prototype design pattern in this implementation can be seen at:
Shape.h, abstract base class so all shapes can be treated the same,
Rectangle.h, derived from Shape,
Circle.h, derived from Shape,
ShapeCache.h, a cache of predefined shapes,
ShapeMain.cpp, the main function.
Common themes in design patterns
Design patterns are used by experts to make their designs more flexible and reusable, so in studying them you tend to see:
Create a design pattern that separates the concerns of an application into three interconnected components: the Model, the View, and the Controller. Each component has its own distinct responsibilities, which helps in achieving modularity and maintainability.
See the The MVC Design Pattern. A solution can be seen at: UserModel.h, UserView.h, UserController.h and MVC.cpp.
The Pizza Builder Revisited
Let us build a pizza without using the builder design pattern. We have a class for a Pizza which can be
HawaiianPizza or SpicyPizza. We have a Cood who is going to build this pizza for us. The following
code shows how to build a pizza without the design pattern. Do you find this code awkward?
Pizza.h, the Pizza base class,
HawaiianPizza.h, derived from Pizza,
SpicyPizza.h, derived from Pizza,
Cook.h, the Cook class, and
Pizza2.cpp, the main function.
As you can see from the code, an enumeration is required to replace the builder class.