Skip to content

Repository files navigation

CREATIONAL

Creational software design pattern deals with object creation mechanisms

Factory Method

Should be applied when:

  • We have a superclass with multiple subclasses and we need to return one of the subclass based on the parameter
  • We want to separates the code of instantiation a class to the Factory class (Loose Coupling)

How it solves the problem:

  • Provides a static method with return-type of superclass or protocol and return a specific object based on the parameter

Example

enumCarType{case lamborghini
case ferrari
}protocolCar{func run()}classLamborghini:Car{func run(){print("\(Lamborghini.self)")}}classFerrari:Car{func run(){print("\(Ferrari.self)")}}classCarFactory{
// return object based on the param
staticfunc create(carType:CarType)->Car{switch carType {case.lamborghini:
// initialization code here
returnLamborghini()case.ferrari:
// initialization code here
returnFerrari()}}}
// Usage
letlamborghini=CarFactory.create(carType:.lamborghini)letferrari=CarFactory.create(carType:.ferrari)

Singleton

Should be applied when:

  • We want to create one object and globally use it the whole time
  • We don’t want to create the same object again and again
  • Do not overuse this pattern 🙂

How it solves the problem:

  • A class contains one global shared instance property
  • Use private constructor to ensure that the object can only be instantiated once

Example

classDatabase{staticletinstance=Database()privateinit(){
// to prevent 2nd initialization
}func write(){print("Writing datatase")}}
// Usage
Database.instance.write()

Builder

Should be applied when:

  • We want to compose complex objects
  • Object contains a lot of properties
  • A constructor has too many parameters, it gets difficult to read and manage

How it solves the problem:

  • Use an inner-class Builder to create the object part-by-part and provide a method that will return the final object

Example

extensionUILabel{classBuilder{
// Component we want to build part by part
privateletlabel=UILabel(frame:.zero)func withBackgroundColor(_ color:UIColor)->Self{
label.backgroundColor = color
returnself}func withTextColor(_ color:UIColor)->Self{
label.textColor = color
returnself}func withFont(_ font:UIFont)->Self{
label.font = font
returnself}func withFrame(_ frame:CGRect)->Self{
label.frame = frame
returnself}func setText(_ text:String)->Self{
label.text = text
returnself}
// Return the final object
func build()->UILabel{return label
}}}
// Usage
letlabel=UILabel.Builder().withFont(.boldSystemFont(ofSize:18)).withBackgroundColor(.green).withTextColor(.white).withFrame(.zero).setText("Builder Pattern").build()

STRUCTURAL

Structural software design pattern deals with class structure mechanisms

Decorator

Should be applied when:

  • We want to attach additional behavior to an object at runtime without affecting the original object
  • We don’t want to modify the object behavior using Inheritance
  • Inheritance sometimes leads to complex and complicated structure
  • Composition over Inheritance :)

How it solves the problem:

  • Wrapping an object with Decorator class

Example

protocolComputer{varcost:Double{get}vardescription:String{get}}
// Class we want to decorate
classDesktopComputer:Computer{varcost:Double{return300}vardescription:String{return"Desktop Computer"}}
// All decorators will inherite this class
classDesktopComputerDecorator:Computer{
// Object to be decorated
letcomputer:Computervarcost:Double{return computer.cost
}vardescription:String{return computer.description
}init(computer:Computer){self.computer = computer
}}
// Decorators
finalclassProcessorUpgrade:DesktopComputerDecorator{overridevarcost:Double{return computer.cost +100}overridevardescription:String{return computer.description +", core i7"}overrideinit(computer:Computer){
super.init(computer: computer)}}finalclassGraphicCardUpgrade:DesktopComputerDecorator{overridevarcost:Double{return computer.cost +50}overridevardescription:String{return computer.description +", NVIDIA GTX 1080"}overrideinit(computer:Computer){
super.init(computer: computer)}}
// Usage
vardesktop:Computer=DesktopComputer()
// decorate
desktop =ProcessorUpgrade(computer: desktop)
desktop =GraphicCardUpgrade(computer: desktop)
// or
//desktop = ProcessorUpgrade(computer: GraphicCardUpgrade(computer: desktop))
print(desktop.description +", $\(desktop.cost)")

Flyweight

Should be applied when:

  • Application uses large number of similar objects
  • We want to minimize memory usage and increase performance

How it solves the problem:

  • Keep a list of caching objects for future use, instead of creating new one every time

Example

finalclassCar{varname:Stringinit(name:String){self.name = name
}}
// Create and cache Cars for future use
finalclassFlyweightCar{
// Flyweight objects
varcars:[String:Car]=[:]
// If brand name already exists, return cache
// Otherwise create and return new one
func getCarByBrand(name:String)->Car{iflet cacheCar =cars[name]{return cacheCar
}else{letcar=Car(name: name)cars[name]= car
return car
}}}
// Usage
letcar=FlyweightCar()
// create new BMW car
letbmw1= car.getCarByBrand(name:"BMW")
// get BMW car from the cache and reuse instead of creating new
letbmw2= car.getCarByBrand(name:"BMW")

About

Design Pattern concept and code written in Swift by KoingDev

Topics

Resources

Stars

9 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages