Creational patterns are design patterns that deal with how an object is created. These patterns create objects in a manner suitable for particular situations.
There are two basic ideas behind creational patterns. The first is encapsulating the knowledge of which concrete types should be created and the second is hiding how the instances of these types are created.
There are five well-known patterns that are a part of the creational pattern category. They are as follows:
In this chapter, we are going to show examples on how to implement the builder, factory method, and singleton patterns in Swift. Let's start off by looking at one of the most controversial and possibly overused design patterns, the singleton pattern.
The use of the singleton pattern is a fairly controversial subject among certain corners of the development community. One of the main reasons for this is that the singleton pattern is probably the most overused and misused pattern. Another reason this pattern is controversial is that the singleton pattern introduces a global state into an application, which provides the ability to change the object at any point within the application. The singleton pattern can also introduce hidden dependencies and tight compiling. My personal opinion is that, if the singleton pattern is used correctly, there is nothing wrong with using it. However, we do need to be careful not to misuse it.
The singleton pattern restricts the instantiation of a class to a single instance for the lifetime of an application. This pattern is very effective when we need exactly one instance to coordinate actions within our application. An example of a good use of a singleton is if our application communicates with a remote device over Bluetooth and we also want to maintain that connection throughout our application. Some would say that we could pass the instance of the connection class from one page to the next, which is essentially what a singleton is. In my opinion, the singleton pattern, in this instance, is a much cleaner solution, because with the singleton pattern any page that needs the connection can get it without forcing every page to maintain the instance. This also allows us to maintain the connection without having to reconnect each time we go to another page.
The problem the singleton pattern is designed to address is when we need one and only one instance of a type for the lifetime of our application. The singleton pattern is usually used when we need centralized management of an internal or external resource, and a single global point of access. Another popular use of the singleton pattern is when we want to consolidate a set of related activities, needed throughout our application, that do not maintain a state, in one place.
In Chapter 5, Let's Extend Some Types, we used the singleton pattern in the text validation example because we only needed one instance of our text validation types throughout the lifetime of our application. In this example, we used the singleton pattern for our text validation types because we wanted to create a single instance of the types that could then be used by all the components of the application without requiring us to create new instances of the types. These text validation types did not have a state that could be changed. They only had methods that performed the validation on the text and constants that defined how to validate the text. While some may disagree with me, I believe types like these are excellent candidates for the singleton pattern because there is no reason to create multiple instances of these types.
There are several ways to implement the singleton pattern in Swift. The way that is presented here uses class constants that were introduced in version 1.2 of Swift. With this method, a single instance of the class is created the first time we access the class constant. We will then use the class constant to gain access to this instance throughout the lifetime of our application. We will also create a private initializer that will prevent external code from creating additional instances of the class.
Let's look at how we implement the singleton pattern with Swift. The following code example shows how to create a singleton class:
class MySingleton { static let sharedInstance = MySingleton() var number = 0 private init() {} }
We can see that, within the MySingleton
class, we created a static constant named sharedInstance
that contains an instance of the MySingleton
class. A static constant can be called without having to instantiate the class. Since we declared the sharedInstance
constant static, only one instance will exist throughout the lifecycle of the application, thereby creating the singleton pattern.
We also created the private initiator that will restrict other code from creating another instance of the MySingleton
class.
Now, let's see how this pattern works. The MySingleton
pattern has another property named number
, which is of the Int
type. We will monitor how this property changes as we use the
sharedInstance
property to create multiple variables of the MySingleton
type, as shown in the following code:
var singleA = MySingleton.sharedInstance var singleB = MySingleton.sharedInstance var singleC = MySingleton.sharedInstance singleB.number = 2 print(singleA.number) print(singleB.number) print(singleC.number) singleC.number = 3 print(singleA.number) print(singleB.number) print(singleC.number)
In this example, we used the sharedInstance
property to create three variables of the MySingleton
type. We initially set the number
property of the second MySingleton
variable (singleB
) to the number 2
. When we printed out the value of the number property for singleA
, singleB
, and singleC
, we saw that the number property for all three equals 2
. We then changed the value of the number property of the third MySingleton
variable (singleC
) to the number 3
. When we printed out the value of the number
property again, we saw that all three now have the value of 3
. Therefore, when we change the value of the number property in any of the instances, the values of all three change because each variable is pointed to the same instance.
In this example, we implemented the singleton pattern using a reference (class) type because we wanted to ensure that only one instance of the type existed throughout our application. If we implemented this pattern with a value type, such as a structure or an enumeration, we would run the risk of there being multiple instances of our type. If you recall, each time we pass an instance of a value type, we are actually passing a copy of that instance, which means that, if we implemented the singleton pattern with a value type, each time we called the sharedInstance
property, we would receive a new copy, which would effectively break the singleton pattern.
The singleton pattern can be very useful when we need to maintain the state of an object throughout our application; however, be careful not to overuse it. The singleton pattern should not be used unless there is a specific requirement (requirement is the keyword here) for having one, and only one, instance of our class throughout the lifecycle of our application. If we are using the singleton pattern simply for convenience, then we are probably misusing it.
Keep in mind that, while Apple recommends that we prefer value types to reference types, there are still plenty of examples, such as the singleton pattern, where we need to use reference types. When we continuously tell ourselves to prefer value types to reference types, it can be very easy to forget that there are times where a reference type is needed. Don't forget to use reference types with this pattern.
Now, let's look at the builder design pattern.
The builder pattern helps us with the creation of complex objects and enforces the process of how these objects are created. With this pattern, we generally separate the creation logic from the complex type and put it in another type. This allows us to use the same construction process to create different representations of the type.
The problem that the builder pattern is designed to address is when an instance of a type requires a large number of configurable values. We could set the configuration options when we create instances of the class but that can cause issues if the options are not set correctly or we do not know the proper values for all of the options. Another issue is the amount of code that may be needed to set all of the configurable options each time we create an instance of our types.
The builder pattern solves this problem by introducing an intermediary known as a builder type. This builder type contains most if not all of the information necessary to create an instance of the original complex type.
There are two methods that we can use to implement the builder pattern. The first method is to have multiple builder types where each of the builder types contains the information to configure the original complex object in a specific way. In the second method, we implement the builder pattern with a single builder type that sets all of the configurable options to a default value and then we would change the values as needed.
In this section, we will look at both ways to use the builder pattern because it is important to understand how each works.
Before we show how we would use the builder pattern, let's look at how to create a complex structure without the builder pattern and the problem we run into.
The following code creates a structure named BurgerOld
and does not use the builder pattern:
struct BurgerOld { var name: String var patties: Int var bacon: Bool var cheese: Bool var pickles: Bool var ketchup: Bool var mustard: Bool var lettuce: Bool var tomato: Bool init(name: String, patties: Int, bacon: Bool, cheese: Bool, pickles: Bool,ketchup: Bool,mustard: Bool,lettuce: Bool,tomato: Bool) { self.name = name self.patties = patties self.bacon = bacon self.cheese = cheese self.pickles = pickles self.ketchup = ketchup self.mustard = mustard self.lettuce = lettuce self.tomato = tomato } }
In the BurgerOld
structure, we have several properties that define which condiments are on the burger and also the name of the burger. Since we need to know which items are on the burgers, and which items aren't, when we create an instance of the BurgerOld
structure, the initializer requires us to define each item. This can lead to some complex initializations throughout our application, not to mention that, if we had more than one standard burger (bacon cheeseburger, cheeseburger, hamburger, and so on), we would need to make sure that each is defined correctly. Let's see how to create instances of the BurgerOld
class:
// Create Hamburger var burgerOld = BurgerOld(name: "Hamburger", patties: 1, bacon: false, cheese: false, pickles: false, ketchup: false, mustard: false, lettuce: false, tomato: false) // Create Cheeseburger var burgerOld = BurgerOld(name: "Cheeseburger", patties: 1, bacon: false, cheese: false, pickles: false, ketchup: false, mustard: false, lettuce: false, tomato: false)
As we can see, creating instances of the BurgerOld
type requires a lot of code. Now, let's look at a better way to do this. In this example, we will show how to use multiple builder types where each type will define the condiments that are on a particular burger. We will begin by creating a BurgerBuilder
protocol that will have the following code in it:
protocol BurgerBuilder { var name: String {get} var patties: Int {get} var bacon: Bool {get} var cheese: Bool {get} var pickles: Bool {get} var ketchup: Bool {get} var mustard: Bool {get} var lettuce: Bool {get} var tomato: Bool {get} }
This protocol simply defines the nine properties that will be required for any type that implements this protocol. Now, let's create two structures that implement this protocol, the HamBurgerBuilder
and the CheeseBurgerBuilder
structures:
struct HamBurgerBuilder: BurgerBuilder { let name = "Burger" let patties = 1 let bacon = false let cheese = false let pickles = true let ketchup = true let mustard = true let lettuce = false let tomato = false } struct CheeseBurgerBuilder: BurgerBuilder { let name = "CheeseBurger" let patties = 1 let bacon = false let cheese = true let pickles = true let ketchup = true let mustard = true let lettuce = false let tomato = false }
In both the HamBurgerBuilder
and the CheeseBurgerBuilder
structures, all we are doing is defining the values for each of the required properties. In more complex types, we might need to initialize additional resources.
Now, let's look at our Burger
structure, which will use instances of the BugerBuilder
protocol to create instances of itself. The following code shows this new Burger
type:
struct Burger { var name: String var patties: Int var bacon: Bool var cheese: Bool var pickles: Bool var ketchup: Bool var mustard: Bool var lettuce: Bool var tomato: Bool init(builder: BurgerBuilder) { self.name = builder.name self.patties = builder.patties self.bacon = builder.bacon self.cheese = builder.cheese self.pickles = builder.pickles self.ketchup = builder.ketchup self.mustard = builder.mustard self.lettuce = builder.lettuce self.tomato = builder.tomato } func showBurger() { print("Name: \(name)") print("Patties: \(patties)") print("Bacon: \(bacon)") print("Cheese: \(cheese)") print("Pickles: \(pickles)") print("Ketchup: \(ketchup)") print("Mustard: \(mustard)") print("Lettuce: \(lettuce)") print("Tomato: \(tomato)") } }
The difference between this Burger
structure and the BurgerOld
structure shown earlier is the initializer. In the previous BurgerOld
structure, the initializer took nine arguments—one for each constant defined in the structure. In the new Burger
structure, the initializer takes one argument, which is an instance of a type that conforms to the BurgerBuilder
protocol. This new initializer allows us to create instances of the Burger
class like this:
// Create Hamburger var myBurger = Burger(builder: HamBurgerBuilder()) myBurger.showBurger() // Create Cheeseburger with tomatos var myCheeseBurgerBuilder = CheeseBurgerBuilder() var myCheeseBurger = Burger(builder: myCheeseBurgerBuilder) // Lets hold the tomatos myCheeseBurger.tomato = false myCheeseBurger.showBurger()
If we compare how we create instances of the new Burger
structure to the earlier BurgerOld
structure, we can see that it is much easier to create instances of the Burger
structure. We also know that we are correctly setting the property values for each type of burger because the values are set directly in the builder classes.
As we mentioned earlier, there is a second method that we can use to implement the builder pattern. Rather than having multiple builder types, we can have a single builder type that sets all of the configurable options to a default value; then we would change the values as needed. I use this implementation method a lot when I am updating older code because it is easy to integrate it with preexisting code.
For this implementation, we will create a single BurgerBuilder
structure. This BurgerBuilder
structure will be used to create instances of the BurgerOld
structure and will, by default, set all of the ingredients to their default values. The BurgerBuilder
structure also give us the ability to change what ingredients will go on the burger prior to creating instances of the BurgerOld
structure. We create the BurgerBuilder
structure like this:
struct BurgerBuilder { var name = "Burger" var patties = 1 var bacon = false var cheese = false var pickles = true var ketchup = true var mustard = true var lettuce = false var tomato = false mutating func setPatties(choice: Int) {self.patties = choice} mutating func setBacon(choice: Bool) {self.bacon = choice} mutating func setCheese(choice: Bool) {self.cheese = choice} mutating func setPickles(choice: Bool) {self.pickles = choice} mutating func setKetchup(choice: Bool) {self.ketchup = choice} mutating func setMustard(choice: Bool) {self.mustard = choice} mutating func setLettuce(choice: Bool) {self.lettuce = choice} mutating func setTomato(choice: Bool) {self.tomato = choice} func buildBurgerOld(name: String) -> BurgerOld { return BurgerOld(name: name, patties: self.patties, bacon: self.bacon, cheese: self.cheese, pickles: self.pickles, ketchup: self.ketchup, mustard: self.mustard, lettuce: self.lettuce, tomato: self.tomato) } }
In the BurgerBuilder
structure, we define our nine properties (ingredients) for our burger and then create a setter method for each of the properties except for the name
property. We also create one method named buildBurgerOld()
that will create an instance of the BurgerOld
structure based on the values of the properties in the BurgerBuilder
instance. We use the BurgerBuilder
structure like this:
var burgerBuilder = BurgerBuilder() burgerBuilder.setCheese(true) burgerBuilder.setBacon(true) var jonBurger = burgerBuilder.buildBurgerOld("Jon's Burger")
In this example, we create an instance of the BurgerBuilder
structure. We then use the setCheese()
and setBacon()
methods to add cheese and bacon to our burger. Finally we call the buildBurgerOld()
method to create the instance of the Burger
Old structure.
As we can see, both methods that were used to implement the builder pattern greatly simplify the creation of our complex type. Both methods also ensured that our instances were properly configured with default values. If you find yourself creating instances of types with very long and complex initialization commands, I would recommend that you look at the builder pattern to see if you can use it to simplify the initialization.
For our last example of a creational pattern, we will look at the factory method pattern.
The factory method pattern uses methods to create instances of objects without specifying the exact type that will be created. These methods are called factory methods. This allows us to pick the type to create at runtime.
I find that the factory pattern is one of the patterns that I use a lot. It is also one of the patterns that developers tend to recognize when they first start reading about design patterns because they have used it in previous projects.
The problem that the factory pattern is designed to solve is when there are multiple types that conform to a single protocol and we need to select the appropriate type to instantiate at run time.
The factory method pattern encapsulates the logic used to select which type to instantiate within a single method. This method exposes only the protocol (or base class) to the code that calls it and does not reveal the details of how a particular type was selected.
To demonstrate how we would use the factory method pattern, we will use the text validation types that we created at the end of Chapter 5, Let's Extend Some Types. In this example, we will create a function that will determine which text validation type to use based on the parameters passed into the factory method by the code that called it. As a refresher, the code for the TextValidationProtocol
protocol and the TextValidationProtocol
protocol extension are shown here:
protocol TextValidationProtocol { var regExFindMatchString: String {get} var validationMessage: String {get} } extension TextValidationProtocol { var regExMatchingString: String { get { return regExFindMatchString + "$" } } func validateString(str: String) -> Bool { if let _ = str.rangeOfString(regExMatchingString, options: .RegularExpressionSearch) { return true } else { return false } } func getMatchingString(str: String) -> String? { if let newMatch = str.rangeOfString(regExFindMatchString, options: .RegularExpressionSearch) { return str.substringWithRange(newMatch) } else { return nil } } }
Within the TextValidationProtocol
protocol, we define two properties named regExFindMatchString
and validationMessage
. Within the protocol extension, we implement one computed property named regExMatchingString
and two methods named validateString()
and getMatchingString()
.
Now let's create three types that conform to the TextValidationProtocol
protocol. These three types will be named AlphaValidation
, NumericValidation
, and AlphaNumericValidation
:
class AlphaValidation: TextValidationProtocol { static let sharedInstance = AlphaValidation() private init(){} let regExFindMatchString = "^[a-zA-Z]{0,10}" let validationMessage = "Can only contain Alpha characters" } class AlphaNumericValidation: TextValidationProtocol { static let sharedInstance = AlphaNumericValidation() private init(){} let regExFindMatchString = "^[a-zA-Z0-9]{0,10}" let validationMessage = "Can only contain Alpha Numeric characters" } class NumericValidation: TextValidationProtocol { static let sharedInstance = NumericValidation() private init(){} let regExFindMatchString = "^[0-9]{0,10}" let validationMessage = "Can only contain only Alpha and Numeric characters." }
The AlphaValidation
class can be used to validate strings to ensure they contain a maximum of 10
alpha characters. The NumericValidation
class can be used to validate strings to ensure they contain a maximum of 10
numeric characters. Finally, the AlphaNumericValidation
class can be used to validate strings to ensure they contain a maximum of 10
alpha or numeric characters.
To use these validation classes, we need a way to determine which class to use based on the string that we are validating. The factory method pattern can help us with this determination. We could implement this pattern as shown in the following code:
func getValidator(alphaCharacters: Bool, numericCharacters: Bool) -> TextValidationProtocol? { if alphaCharacters && numericCharacters { return AlphaNumericValidation.sharedInstance } else if alphaCharacters && !numericCharacters { return AlphaValidation.sharedInstance } else if !alphaCharacters && numericCharacters { return NumericValidation.sharedInstance } else { return nil } }
The getValidator()
method accepts two parameters both of the Boolean type named alphaCharacters
and numericCharacters
. These parameters define the type of validation we wish to do and we then return a type that conforms to the TextValidationProtocol
protocol based on the values in the parameters.
One of the biggest advantages that we get with this pattern is that all of the logic on how the text validation types are selected is encapsulated in this one function. This means that, if we need to change the logic or change which type to use, the only code we need to change is the code within this function rather than having to refactor our entire code base. As an example, if we wish to replace the AlphaValidation
class with a new AlphaSpacesValidation
class, the only code that needs to change is within this function.
We can now use the getValidator()
method, as shown in the following code:
var str = "abc123" var validator1 = getValidator(true, numericCharacters: false) print("String validated: \(validator1?.validateString(str))") var validator2 = getValidator(true, numericCharacters: true) print("String validated: \(validator2?.validateString(str))")
In this code, the validator1
variable contains an instance of the AlphaValidation
type. When we call the validateString()
method of this instance, it returns a false value because the str
variable contains numeric values. The validator2
variable contains an instance of the AlphaNumericValidation
type. When we call the validateString()
method of this instance, it returns true
because the validation class looks for both alpha and numeric characters.
One of the key ideas behind creational patterns is that we take the logic about how and what to create out of our general code base and put it into specific classes or functions. Then, when we need to make changes to our code in the future, the logic is encapsulated in a single spot and can be easily changed, rather than having the logic spread throughout our code.
Now, let's look at structural design patterns.