Swift s Majid

moc Delegovat design vzor

29. Května 2019

Poslední týden před WWDC a všichni tak nadšený z nových funkcí, které budeme mít jen za pár dní. Ponechme si však příspěvky související s WWDC na příští týden. Tento týden budeme mluvit o mém oblíbeném delegátovi designu. Delegát je nejpřímější a nejsilnější vzor.

V softwarové inženýrství, delegace vzor je objektově orientovaný návrhový vzor, který umožňuje objekt, složení, aby se dosáhlo stejného kódu znovu použít jako dědictví. V delegování objekt zpracovává požadavek delegováním na druhý objekt (delegát). Delegát je pomocný objekt, ale s původním kontextem.

protokoly

používáme delegát vzor každý den, a iOS SDK jej používá na mnoha místech. Například UITableView deleguje na uitableviewdatasource vyplňující tabulku buňkami, deleguje také výběr buněk a další akce na UITableViewDelegate. Dalším vynikajícím příkladem delegátů je FlowController nebo koordinátoři. ViewControllers deleguje logiku navigace na koordinátora. Oddělil jsem příspěvek o extrahování navigační logiky do Flowcontrollerů.

pojďme se ponořit do vzorků kódu. Předpokládejme, že pracujete na hře. Vytěžili jste herní logiku do oddělené třídní hry a chcete delegovat změny stavu hry na UIViewController, který tuto hru vykresluje.

protocol GameDelegate: AnyObject { func stateChanged(from oldState: Game.State, to newState: Game.State)}class Game { private var state: State = .notStarted { didSet { delegate?.stateChanged(from: oldValue, to: state) } } weak var delegate: GameDelegate? private(set) var value: Int = 0 func start() { state = .started } func generateNextValue() { value = Int.random(in: 0..<1000) state = generateState(using: value) }}extension Game { enum State { case notStarted case started case right case win case lost }}

zde je zdrojový kód jednoduché hry, která generuje náhodné hodnoty. Herní engine generuje stav na základě náhodných hodnot. Každá změna stavu volání delegáta projít staré a nové stavy. Definujeme náš delegát protokol rozšířený z AnyObject, to znamená, že jediná instance třídy jej může přijmout. Také používám slabé Klíčové slovo k definování delegáta držení proměnné. Bylo potřeba přerušit retain cyklus mezi delegátem a herní třídou. Pojďme se nyní podívat na GameViewController.

class GameViewController: UIViewController { private let game: Game init(game: Game) { self.game = game super.init(nibName: nil, bundle: nil) } @IBAction func play() { game.start() } @IBAction func next() { game.generateNextValue() } override func viewDidLoad() { super.viewDidLoad() game.delegate = self }}extension GameViewController: GameDelegate { func render(_ state: Game.State) { switch state { case .lost: renderLost() case .right: renderRight() case .win: renderWin() case .started: renderStart() case .notStarted: renderNotStarted() } } func stateChanged(from oldState: Game.State, to newState: Game.State) { render(newState) }}

Zde máme GameViewController třídy, který se živí hru s akce uživatele a zobrazovat změny stavu. GameViewController odpovídá GameDelegate a implementuje všechny potřebné Vykreslování v rozšíření. Výsledkem je složitelná kódová základna pomocí delegáta design pattern.

uzávěry

někdy, když máte v delegátu pouze jednu metodu, můžete ji nahradit uzávěrem. Myšlenka je stejná, ale nyní zavoláte uzavření a předáte stav místo volání metody protokolem. Podívejme se na příklad s uzavřením.

class Game { typealias StateHandler = (State) -> Void var handler: StateHandler? private var state: State = .notStarted { didSet { handler?(state) } }}class GameViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() game.handler = { state in self?.render(state) } }}

jak vidíte, předáme uzavření instanci herní třídy, která zpracovává změny stavu. We use week to break the retain cycle during closure ‚ s context capture. Další možností zde může být využití skutečnosti, že jakákoli funkce Swift je uzávěrem. Takže místo vytvoření odděleného uzávěru můžeme předat název funkce. Buďte však opatrní, tato metoda vytváří zachovat kruh. Zde je příklad toho, jak to můžeme udělat.

class GameViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() game.handler = render }}extension GameViewController { func render(_ state: Game.State) { switch state { case .lost: renderLost() case .right: renderRight() case .win: renderWin() case .started: renderStart() case .notStarted: renderNotStarted() } }}

Závěr

Dnes jsme diskutovali nejmocnější a jednoduchý design vzor v iOS vývoj. Baví mě, jak je to jednoduché a jak užitečné může být při skládání kusů, aby se codebase oddělil. Neváhejte mě sledovat na Twitteru a zeptejte se na své otázky týkající se tohoto příspěvku. Děkujeme za přečtení a uvidíme se příští týden!



+