Ciao a tutti cari amici di iProg oggi inizieremo una nuova serie di tutorial dedicati ai design pattern in ambiente iOS (ovviamente tali nozioni sono applicabili in qualsiasi ambiente di progettazione del software). Ma cosa sono i design pattens? Essi non sono altro che soluzioni che possono essere riutilizzate per risolvere problemi ricorrenti, essi possono essere suddivisi in tre principali categorie:
- Creational Pattern: sono responsabili del meccanismi di creazioni di un oggetto ovvero astraggono il processo di istanziazione di quest’ultimo.
- Structural Pattern: riguardano le modalità con cui classi e oggetti vengono aggregati allo scopo di formare entità più complesse
- Behavioral Pattern: descrive come un oggetto comunica con un altro oggetto
Singleton Pattern
Il singleton è un design pattern creazionale che ha lo scopo di garantire che una determinata classe venga creata una e una sola istanza, e di fornire un punto di accesso globale a tale istanza(Wikipedia). Un esempio di utilizzo di questo pattern potrebbe essere quello di un player audio in quanto instanziare piu’ di un’instanza di quest’ultima potrebbe essere problematico.
Vediamo un esempio pratico di come implementare questo pattern in Swift
[swift]
public class Singleton {
private var names = [String]()
public static let shared: Singleton = {
let instance = Singleton()
return instance
}()
private init () {}
public func add(name: String) { names.append(name)}
public func retrieve(name: String) -> String {
if let index = names.index(of: name) { return names[index] }
return "The name \(name) is not found into array"
}
}
Singleton.shared.add(name: "Diego")
[/swift]
Come possiamo notare dall’esempio per poter utilizzare la classe non la instanziamo “direttamente” ma utilizziamo l’instanza presente nella classe stessa questo fa si che avremo una sola instanza di quest’ultima (shared) all’interno del nostro progetto, tuttavia in esempi piu’ complessi bisogna far attenzione in quanto in ambiente multithreading si potrebbe verificare qualche errore durante la lettura/scrittura, quindi e’ necessario utilizzare una qualche sorta di “preacauzione” come ad esempio creare un coda e utilizzare il Dispatch barriers.
Prototype Pattern
Prototype è un design pattern creazionale che permette di creare nuovi oggetti “clonando” un oggetto iniziale, detto appunto prototipo. Questo design pattern dovrebbe essere utilizzato quando instanziare un determinato oggetto e’ “dispendioso” ad esempio se stiamo creando un videogame potremmo utilizzarlo per instanziare nuovi “mostri/nemici”.
In Swift i value type sono “Copy” per default in questo modo possiamo copiare due instanze senza nessun problema. La maniera piu’ semplice per poter implementare questo pattern in swift e’ quella di utilizzare le Struct in quanto ogni volta che copiamo un instanza e’ come se ne fosse creata una nuova infatti non punteranno allo stesso indirizzo di memoria.
[swift]
public struct Person: Equatable {
var firstName: String
var lastName: String
public static func ==(rhs: Person, lhs: Person) -> Bool {
return rhs.firstName == lhs.firstName && rhs.lastName == lhs.lastName
}
}
var prototipo = Person (firstName: "Franco", lastName: "Castaldo")
print(prototipo == clone)
var clone = prototipo
clone.firstName = "Luigi"
print(prototipo == clone)
[/swift]
Se invece volessimo utilizzare una classe dobbiamo implementare il protocollo NSCopying
[swift]
public class Person: Equatable, NSCopying {
var firstName: String
var lastName: String
init(firstName:String, lastName:String) {
self.firstName = firstName
self.lastName = lastName
}
public func copy(with zone: NSZone? = nil) -> Any {
return Person(firstName: self.firstName, lastName: self.lastName)
}
public static func ==(rhs: Person, lhs: Person) -> Bool {
return rhs.firstName == lhs.firstName && rhs.lastName == lhs.lastName
}
}
var prototipo = Person(firstName: "Franco", lastName: "Castaldo")
var clone = prototipo.copy()
[/swift]
Builder Pattern
Il design pattern Builder,separa la costruzione di un oggetto complesso dalla sua rappresentazione cosicché il processo di costruzione stesso possa creare diverse rappresentazioni.L’algoritmo per la creazione di un oggetto complesso è indipendente dalle varie parti che costituiscono l’oggetto e da come vengono assemblate.(Wikipedia).
Un primo approccio potrebbe quello di utilizzare i valori di default all’interno del costruttore
[swift]
class Setting {
let backGroungColor: UIColor
let font: UIFont
init(backGroungColor:UIColor = .white , font: UIFont = UIFont.systemFont(ofSize: 15)) {
self.backGroungColor = backGroungColor
self.font = font
}
}
let alert = Setting() // costruisce l’oggetto con valori di default
let popUP = Setting(backGroungColor: .red, font: UIFont.systemFont(ofSize: 20))
[/swift]
o in alternativa e’ possibile creare un vero e proprio oggetto “builder”
[swift]
class Setting {
let backGroungColor: UIColor
let font: UIFont
init(backGroungColor:UIColor , font: UIFont) {
self.backGroungColor = backGroungColor
self.font = font
}
}
class SettingBuilder {
var backGroungColor = UIColor.white
var font: UIFont = UIFont.systemFont(ofSize: 15)
public init () {}
public var setting: Setting {
get {
return Setting(backGroungColor: self.backGroungColor, font: self.font)
}
}
}
let builder = SettingBuilder()
let defaultSetting = builder.setting
let label = UILabel()
label.backgroundColor = defaultSetting.backGroungColor
label.font = defaultSetting.font
[/swift]
Factory Pattern
Nella programmazione ad oggetti, il Factory Method è uno dei design pattern fondamentali per l’implementazione del concetto di factory. Come altri pattern creazionali, esso indirizza il problema della creazione di oggetti senza specificarne l’esatta classe. Questo pattern raggiunge il suo scopo fornendo un’interfaccia per creare un oggetto, ma lascia che le sottoclassi decidano quale oggetto istanziare.(Wikipedia)
[swift]
enum SettingType {
case titolo
case sottotitolo
}
protocol Setting {
var font: UIFont { get }
var textColor: UIColor { get }
}
struct Titolo: Setting {
var font: UIFont { return UIFont.systemFont(ofSize: 20) }
var textColor: UIColor { return .black }
}
struct SottoTitolo: Setting {
var font: UIFont { return UIFont.systemFont(ofSize: 15) }
var textColor: UIColor { return .gray }
}
class Factory {
class func factorySetting(setting: SettingType) -> Setting {
switch setting {
case .titolo: return Titolo()
case .sottotitolo: return SottoTitolo()
}
}
}
let titoloLabel = UILabel()
let titolo = Factory.factorySetting(setting: .titolo)
titoloLabel.textColor = titolo.textColor
titoloLabel.font = titolo.font
[/swift]
Nel prossimo articolo esploreremo gli Structural Pattern.