Design patterns by Tutorials— The power of OOP (part 1)
Journey of Design Patterns--The Power of OOP 1
Design patterns by Tutorials— The power of OOP (part 1)
Prerequisites — This blog series requires an intermediate level of expertise in object-oriented programming. You should have basic knowledge about class, object, constructor, inheritance, value and reference type. An intermediate will gain knowledge and experts will sharpen his or her knowledge by reading this series from start to finish. Prerequisites – This blog series requires intermediate expertise in object-oriented programming. You should have a basic understanding of classes, objects, constructors, inheritance, value and reference types. An intermediate will gain knowledge and an expert will sharpen his or her knowledge reading this series from start to finish.
Design patterns are used to represent the best practices adopted by the experienced object-oriented software developer community. Design patterns are used to represent best practices adopted by a community of experienced object-oriented software developers.
The builder design pattern helps us to build an object in a more simpler and readable way. The builder design pattern follows two simple rules as mentioned below. The builder design pattern helps us build objects in a simpler and readable way. The builder design pattern follows two simple rules, described below.
-
Separate the original class representation and its construction methods. Separate the original class representation and its constructor.
-
returns the instance of the class in the final step Return an instance of the class in the final step
The best example of a builder design pattern is SwiftUI, yes you read right. SwiftUI uses builder design pattern for most of its classes e.g. Text, Image The best example of builder design pattern is SwiftUI, yes you read that right. Most classes in SwiftUI use the builder design pattern, such as text, images, etc.
Problem: Question:
Think about the class say Person having ten or more properties, imaging its constructor design when you need to create an instance of the class Person. Its constructor will take ten or more arguments, it will be hard to manage these many arguments as a single function or constructor and eventually, you will lose the readability of code. checkout the below example. Think about this class, say Person having ten or more properties, when you need to create an instance of Person class, imagine its constructor design. Its constructor requires 10 or more parameters, which are difficult to manage as a function or constructor, and eventually, you lose the readability of your code. Check out the example below.
WithoutDesignPatternExample1.swift:
class Person {
//personal details
var name: String = ""
var gender: String = ""
var birthDate: String = ""
var birthPlace: String = ""
var height: String = ""
var weight: String = ""
//contact details
var phone: String = ""
var email: String = ""
//address details
var streeAddress: String = ""
var zipCode: String = ""
var city: String = ""
//work details
var companyName: String = ""
var designation: String = ""
var annualIncome: String = ""
//constructor
init(name: String,
gender: String,
birthDate: String,
birthPlace: String,
height: String,
weight: String,
phone: String,
email: String,
streeAddress: String,
zipCode: String,
city: String,
companyName: String,
designation: String,
annualIncome: String) {
self.name = name
self.gender = gender
self.birthDate = birthDate
self.birthPlace = birthPlace
self.height = height
self.weight = weight
self.phone = phone
self.email = email
self.streeAddress = streeAddress
self.zipCode = zipCode
self.height = height
self.city = city
self.companyName = companyName
self.designation = designation
self.annualIncome = annualIncome
}
}
//This is function in Xcode-Playground which executes our test code
func main() {
let hitendra = Person(name: "Hitendra Solanki",
gender: "Male",
birthDate: "2nd Oct 1991",
birthPlace: "Gujarat, India",
height: "5.9 ft",
weight: "85kg",
phone: "+91 90333-71772",
email: "hitendra.developer@gmail.com",
streeAddress: "52nd Godrej Street",
zipCode: "380015",
city: "Ahmedabad",
companyName: "Fortune 500",
designation: "Software architect",
annualIncome: "45,000 USD")
//use of Person object
print("\(hitendra.name) works in \(hitendra.companyName) compay as a \(hitendra.designation).")
}
//call main to execute our test code in Xcode-Playground
main()
/* Console output:
Hitendra Solanki works in Fortune 500 compay as a Software architect.
*/
Try to run the above example in the playground, it will run successfully and give you an expected output. Logically it is correct. Try running the above example on the playground, it will run successfully and give you an expected output. Logically correct.
We can improve the above example, by overcoming the below points. We can improve the above example by overcoming the following points.
-
We have to pass the values in a mentioned order, can not reorder the parameters sequence to improve the readability. We must pass the values in the above order, the parameter sequence cannot be reordered to improve readability.
-
We have to pass all the values, even if we don’t know some values at the time of object creation. We must pass all values, even if we don’t know some values when creating the object.
E.g. Suppose you required to create an object of Person class, but the person is still finding a job. When that person will join any company then only we will have works details. Suppose you need to create an object of class Person, but the object is still looking for work. When that person will join any company, then only we will have the job details.
Solution:
Solution:
-
Create logical groups of related properties. Create logical groups of related properties.
-
Create separate builder classes for different groups of properties[helps in properties normalization, this is optional. Creating separate generator classes for different attribute groups [helps with attribute normalization, this is optional.
-
Retrieve an instance in the final step. An instance is retrieved in the final step.
Let’s simplify this with an example, Let us simplify it with an example,
We already have a class named Person in example WithoutDesignPatternExample1.swift, in which we have 14 properties. If we closely check all the 14 properties, the properties have 4 logical groups. In example, we already have a class called Person without designpatternexample1. Swift, we have 14 properties. If we carefully examine all 14 properties, there are 4 logical groups of these properties.
- Personal details properties Personal information attributes
2.Contact details properties Contact information properties
3.Address details properties Address details properties
- Company details properties Company details properties
Faceted and Fluent design pattern together helps us to overcome the two problems mentioned above. The Faceted and Fluent design patterns together help us overcome the two problems mentioned above.
BuilderDesignPattern[Faceted+Fluent]Example1.swift:
//This is function in playground which executes our test code
func main() {
var hitendra = Person() //person with empty details
let personBuilder = PersonBuilder(person: hitendra)
hitendra = personBuilder
.personalInfo
.nameIs("Hitendra Solanki")
.genderIs("Male")
.bornOn("2nd Oct 1991")
.bornAt("Gujarat, India")
.havingHeight("5.9 ft")
.havingWeight("85 kg")
.contacts
.hasPhone("+91 90333-71772")
.hasEmail("hitendra.developer@gmail.com")
.lives
.at("52nd Godrej Street")
.inCity("Ahmedabad")
.withZipCode("380015")
.build()
//use of Person object
print("\(hitendra.name) has contact number \(hitendra.phone) and email \(hitendra.email)")
//later on when we have company details ready for the person
hitendra = personBuilder
.works
.asA("Software architect")
.inCompany("Fortune 500")
.hasAnnualEarning("45,000 USD")
.build()
//use of Person object with update info
print("\(hitendra.name) works in \(hitendra.companyName) compay as a \(hitendra.designation).")
}
//call main to execute our test code
main()
//Person class which only contains the details
class Person {
//personal details
var name: String = ""
var gender: String = ""
var birthDate: String = ""
var birthPlace: String = ""
var height: String = ""
var weight: String = ""
//contact details
var phone: String = ""
var email: String = ""
//address details
var streeAddress: String = ""
var zipCode: String = ""
var city: String = ""
//work details
var companyName: String = ""
var designation: String = ""
var annualIncome: String = ""
//empty constructor
init() { }
}
//PersonBuilder class helps to construct the person class instance
class PersonBuilder {
var person: Person
init(person: Person){
self.person = person
}
//personal details builder switching
var personalInfo: PersonPersonalDetailsBuilder {
return PersonPersonalDetailsBuilder(person: self.person)
}
//contact details builder switching
var contacts: PersonContactDetailsBuilder {
return PersonContactDetailsBuilder(person: self.person)
}
//address details builder switching
var lives: PersonAddressDetailsBuilder {
return PersonAddressDetailsBuilder(person: self.person)
}
//work details builder switching
var works: PersonCompanyDetailsBuilder {
return PersonCompanyDetailsBuilder(person: self.person)
}
func build() -> Person {
return self.person
}
}
//PersonPersonalDetailsBuilder: update personal details
class PersonPersonalDetailsBuilder: PersonBuilder {
func nameIs(_ name: String) -> Self {
self.person.name = name
return self
}
func genderIs(_ gender: String) -> Self {
self.person.gender = gender
return self
}
func bornOn(_ birthDate: String) -> Self {
self.person.birthDate = birthDate
return self
}
func bornAt(_ birthPlace: String) -> Self {
self.person.birthPlace = birthPlace
return self
}
func havingHeight(_ height: String) -> Self {
self.person.height = height
return self
}
func havingWeight(_ weight: String) -> Self {
self.person.weight = weight
return self
}
}
//PersonContactDetailsBuilder: update contact details
class PersonContactDetailsBuilder: PersonBuilder {
func hasPhone(_ phone: String) -> Self {
self.person.phone = phone
return self
}
func hasEmail(_ email: String) -> Self {
self.person.email = email
return self
}
}
//PersonAddressDetailsBuilder: update address details
class PersonAddressDetailsBuilder: PersonBuilder {
func at(_ streeAddress: String) -> Self {
self.person.streeAddress = streeAddress
return self
}
func withZipCode(_ zipCode: String) -> Self {
self.person.zipCode = zipCode
return self
}
func inCity(_ city: String) -> Self {
self.person.city = city
return self
}
}
//PersonCompanyDetailsBuilder: update company details
class PersonCompanyDetailsBuilder: PersonBuilder {
func inCompany(_ companyName: String) -> Self {
self.person.companyName = companyName
return self
}
func asA(_ designation: String) -> Self {
self.person.designation = designation
return self
}
func hasAnnualEarning(_ annualIncome: String) -> Self {
self.person.annualIncome = annualIncome
return self
}
}
/* Console output:
Hitendra Solanki has contact number +91 90333-71772 and email hitendra.developer@gmail.com
Hitendra Solanki works in Fortune 500 compay as a Software architect.
*/
In the above example, we have separated the responsibilities of Person class in different classes. Person class now only holds the data properties, while we have created the multiple builder classes having responsibilities to build/update the relative group of properties. In the above example, we divided the responsibilities of the Person class into different classes. The Person class now only contains data attributes, and we have created multiple builder classes that are responsible for building/updating related attribute groups.We have one base builder class PersonBuilder and have four more derived builder classes named PersonPersonalDetailsBuilder, PersonContactDetailsBuilder, PersonAddressDetailsBuilder and PersonCompanyDetailsBuilder. We have a basic builder class PersonBuilder, and four derived builder classes, namely PersonPersonalDetailsBuilder, PersonContactDetailsBuilder, PersonAddressDetailsBuilder and PersonCompanyDetailsBuilder.
The base class PersonBuilder helps us to switch between multiple builders anytime while other four builders derived from PersonBuilder have responsibilities to update relative properties. The base class PersonBuilder helps us switch between multiple builders at any time, while the other four builders derived from PersonBuilder are responsible for updating related properties.
In the above example, we can clearly see that the construction of a Person object is more readable compared to our first example WithoutDesignPatternExample1.swift also we can update the group of properties or any single property at any time in a more readable manner. In the above example, we can clearly see that the structure of the Person object is much more readable compared to our first example.

In the above example, note that we are returning the builder instance its self after calling every property update method. Which helps us to write chaining of multiple methods of the same builder instead of writing multiple lines separately. This concept is known as Fluent pattern. In the above example, note that after calling each property update method, we return the generator instance itself. This helps us write chaining of multiple methods of the same generator instead of writing multiple separate lines. This concept is called coherence mode.
Benefits:
Benefits:
-
Easy initialize an object of a class having too many properties in a more readable manner. Easily initializes an object of a class with too many properties, for better readability.
-
Follows the single responsibility principle. Follow the single responsibility principle.
-
Initialize object or update properties in any order as per your convenience. Initialize objects or update properties in any order as per your convenience.
Bonus:
Bonus:
To make the builder pattern consistent across the whole project, you can create protocol as below. To make the generator pattern consistent throughout your project, you can create a protocol like the following.

Original text: https://medium.com/flawless-app-stories/design-patterns-by-tutorials-the-power-of-oop-2e871b551cbe
读完之后,下一步看什么
如果还想继续了解,可以从下面几个方向接着读。