The template method pattern is a simple pattern used when you need a general behavior but where the details of the algorithm must be specific to subclasses.
The template method pattern isolates various parts of an algorithm. The algorithm skeleton is defined in an abstract class where some steps of the algorithm are delegated to its subclasses and some others are fixed in the abstract class itself and cannot be overridden in subclasses.
The participants of this pattern are as follows:
AbstractClass
, which defines the template method, and the signature of the sub parts of the algorithm are invoked by the template method.ConcreteClass
implements abstract methods used by the template method of the AbstractClass
. It is possible to have several concrete classes.The algorithm defined in the template method is called TemplateMethod()
in the generic UML class diagram and invokes parts of the algorithm in the subclasses.
You are working on a new simulation game with several personage types. Each personage has several properties, such as money, happiness, fatigue, hungry, and knowledge.
Each of these personages can "Play" a day. A day is decomposed in several parts:
GetUp
EatBreakFast
DoWashingUp
GoToWork
Work
GoHome
DoPersonalActivites
EatDinner
Sleep
We have three types of personages: Student
, Searcher
, and FireMan
; each of them can "play" a day but doesn't react in the same way depending on the day phase.
So, we will override parts of the algorithm in the concrete class by defining the personage type. The only part of the algorithm that is fixed is the DoWashingUp
function. This part will not and cannot be overridden in subclasses.
Open the TemplateMethod project with Xcode. The project is quite simple. We will find the TemplateMethod
folder in the AbstractPersonage.Swift
class and all concrete subclasses that implement parts of the algorithm in the three concrete classes: Searcher
, Student
, and FireMan
:
To implement the preceding example, we will first prepare our abstract class that defines a personage. Remember to consider this class as an abstract class. You must not instantiate it directly to your code, but you must instantiate only a subclass of AbstractPersonage
:
class AbstractPersonage { private final var fatigue = 100 private final var money = 0 private final var happiness = 100 private final var hungry = 100 private final var knowledge = 100 private final var name:String! final var canBePaid: Bool = true required init(name: String) { self.name = name } func toString() { print("("Name: (name) / fatigue : (Fatigue) / happiness (Happiness) / Hungry (Hungry) / knowledge (Fatigue) / money: (Money) / ") } //Play a day for the Personage func playDay() { print("PLAYING DAY") print("Get Up!") getUp() print("Eat Breakfast") eatBreakfast() doWashingUp() print("Go to work") goToWork() print("Work") work() if canBePaid { print("Receive Pay") getPaid() } print("BackHome") backToHome() print("Do personal activities") doPersonalActivities() print("Eat dinner") eatDinner() print("Sleep") sleep() } func getUp() { Fatigue = 0 Happiness = 25 Hungry = -25 Knowledge = 0 } func eatBreakfast() { Fatigue = -5 Happiness = 25 Hungry = 60 Knowledge = 0 } final func doWashingUp() { print("do washing up") } func goToWork() { Fatigue = -15 Happiness = -15 Hungry = -10 Knowledge = 0 } func work(){ Fatigue = -40 Happiness = -25 Hungry = -40 Knowledge = 25 } func getPaid() { Money = 1000 } func backHome() { Fatigue = -15 Happiness = 10 Hungry = -10 Knowledge = 0 } func doPersonalActivities() { Fatigue = -15 Happiness = 15 Hungry = -10 Knowledge = 0 } func eatDinner() { Fatigue = -10 Happiness = 5 Hungry = 40 Knowledge = 0 } func sleep() { Fatigue = 90 Happiness = 0 Hungry = -5 Knowledge = 2 } var Fatigue: Int { get{ return fatigue } set{ fatigue += newValue } } var Hungry: Int { get{ return hungry } set{ hungry += newValue } } var Happiness: Int { get{ return happiness } set{ happiness += newValue } } var Money: Int { get{ return money } set{ money += newValue } } var Knowledge: Int { get{ return knowledge } set{ knowledge += newValue } } }
In the preceding code, we can distinguish three parts. The first part is a private variable declaration. We mark the access modifier to avoid modification in subclasses:
private final var fatigue = 100 private final var money = 0 …
Our playDay
template method invokes all the parts of the algorithm:
//Play a day for the Personage func playDay() { print("PLAYING DAY") print("Get Up!") getUp() print("Eat Breakfast") eatBreakfast() doWashingUp() print("Go to work") goToWork() print("Work") work() if canBePaid { print("Receive Pay") getPaid() } print("BackHome") backHome() print("Do personal activities") doPersonalActivities() print("Eat dinner") eatDinner() doWashingUp() print("Sleep") sleep() } …
Then, we define the method signatures that are parts of the algorithm and we will eventually implement them. Here, we define a default implementation of each method:
func eatBreakfast() { Fatigue = -5 Happiness = 25 Hungry = 60 Knowledge = 0 } func goToWork() { Fatigue = -15 Happiness = -15 Hungry = -10 Knowledge = 0 } //others methods
Finally, we define our computed properties to modify the setter behavior by making an addition to itself when a new value is assigned to the property:
var Fatigue: Int { get{ return fatigue } set{ fatigue += newValue } } var Hungry: Int { get{ return hungry } set{ hungry += newValue } }
The following two steps make our sample better:
required init(name: String) { self.name = name }
toString()
method that will print all the properties and values of the personage:func toString() { print("Name: (name) / fatigue : (Fatigue) / happiness (Happiness) / Hungry (Hungry) / knowledge (Fatigue) / money: (Money) / ") }
Well, our abstract class that implements the template method is complete. Now, we have a skeleton to make a new concrete personage, for example, a student.
The student doesn't have a job, so he won't get paid. The student reads books during his personal activities.
So, we will create a new Student
class that implements our abstract class that contains the template method, and we will override only parts of the algorithm that changes in the parent class:
class Student: AbstractPersonage { required init(name: String) { super.init(name: name) //student cannot be paid canBePaid = false } override func doPersonalActivities() { //student Read Books during its personal activities //so life indicators must be updated Fatigue = -5 Happiness = 15 Hungry = -5 Knowledge = 15 } }
In the same way, we define the Searcher
and FireMan
classes that implement our abstract classes and both can be paid but not the same amount. Also, each of them must override some parts of the algorithm to be more accurate with specificity of the entity that the class represent:
For the Searcher
class, we will implement the AbstractPersonage
protocol as follows:
class Searcher: AbstractPersonage { override func getPaid() { Money = 3000/30 } override func sleep() { //Searcher sleep very well Fatigue = 90 Happiness = 0 Hungry = -5 Knowledge = 10 } override func doPersonalActivitie() { //Searcher Read ScientificBooks during its personal activities //so life indicators must be updated Fatigue = -5 Happiness = 10 Hungry = -5 Knowledge = 25 } }
For the FireMan
class, we will implement it as follows:
import Foundation class FireMan: AbstractPersonage { override func getPaid() { Money = 2500/30 } override func sleep() { //FireMan doesn't sleep a lot Fatigue = 80 Happiness = 5 Hungry = -5 Knowledge = 0 } override func doPersonalActivities() { //FireMan makes lot of sports during personal activities //so life indicators must be updated Fatigue = -10 Happiness = 5 Hungry = -5 Knowledge = 15 } override func work() { Fatigue = -25 Happiness = -55 Hungry = -45 Knowledge = 10 } }
Our template method and concrete classes are now ready. We can now write in the main.swift
file. Our simple client will instantiate a student called Simon
, a searcher called Natasha
, and a fireman called Edward
.
We will display the properties of each of them before simulating 30 days of their life. Then, we will tell these three personages to live for 30 days using the following code:
student.toString() searcher.toString() fireMan.toString()
Then, we will play 30 days of life in a for
loop:
for i in 1...30{ student.playDay() searcher.playDay() fireMan.playDay() }
At the end of these 30 days of life, we will check the properties of each of them:
print("- **** 30 days later:") student.toString() searcher.toString() fireMan.toString()
The final code is as follows:
import Foundation let student = Student(name: "Simon") let searcher = Searcher(name: "Natasha") let fireMan = FireMan(name:"Edward") print("- **** Starting with:") student.toString() searcher.toString() fireMan.toString() //Play a month for i in 1...30{ print("**************") print("Play Day (i) ") print("**************") student.playDay() searcher.playDay() fireMan.playDay() } print("- **** 30 days later:") student.toString() searcher.toString() fireMan.toString()
Click on the Run button. On the console, you will see the results after 30 days of life: