WWDC 2015 - Session 408 - iOS, OS X, watchOS
本文核心基于 WWDC 主題演講內(nèi)容,結(jié)合我自己的一些想法,大家可以當(dāng)成翻譯看。

Back to then
假設(shè)我們要設(shè)計(jì)一個(gè)畫板應(yīng)用,核心的模塊應(yīng)該是可繪制對象和渲染引擎,我分別稱它們?yōu)?code>Drawable, Renderer。我們知道所有可繪制對象都應(yīng)該有一個(gè)方法來執(zhí)行繪制操作,也就是說不管矩形、圓形還是其他圖形都應(yīng)該有一個(gè)可以統(tǒng)一調(diào)用的 render,于是很容易想到的就是聲明一個(gè)抽象類,里面有一個(gè)未實(shí)現(xiàn)的 render 函數(shù),但是這里我并不想使用 class,而是使用 struct,為什么呢?因?yàn)閳D形都是由基本值類型構(gòu)成的,而且功能比較單一。有關(guān)于引用類型和值類型的最佳實(shí)踐我會再開一篇文章來寫(同樣也是 WWDC 的內(nèi)容)。
首先我們寫一個(gè)矩形類型:

下面我們設(shè)計(jì)渲染類,為了方便測試圖形繪圖的行為,我們先寫一個(gè)“假的”渲染器,來向控制臺輸出調(diào)試語句:

但是我會說,這是個(gè)很差的設(shè)計(jì),它很難被重構(gòu)、擴(kuò)展、推廣。如果我想用 CoreGraphics 渲染怎么辦?用 QuartzCore 呢?以后推廣到 X Server 呢(雖然有點(diǎn)扯)?逐一重寫?
Now, let's Protocol-Oriented Programming
回到標(biāo)題,還是用協(xié)議,統(tǒng)一所有的接口方法:

那么上面的測試渲染器就可以遵守這個(gè)協(xié)議來寫實(shí)現(xiàn)了:

下面介紹一下 extension 在協(xié)議方面的應(yīng)用,現(xiàn)在我們來寫基于 CoreGraphics 的實(shí)現(xiàn),也就是實(shí)現(xiàn)一個(gè)渲染器,它可以操作 CGContext 來畫圖,繼承 CGContext?為什么不擴(kuò)展它呢?

通過 extension 關(guān)鍵字我們可以將一個(gè)現(xiàn)有的類擴(kuò)展到遵守一個(gè)協(xié)議。隨手寫一個(gè) UIView 來顯示繪制內(nèi)容,這里就不解釋了。
下面測試一下吧!

Hold on
下面我們再寫一個(gè)圓形類和組合類吧。組合類沒什么好說的,說說圓形類。
圓形類型的定義我這么寫的:

然后看看結(jié)果:

誒,什么鬼?怎么有條線啊....
因?yàn)?lineTo 完畢后起點(diǎn)在矩形左上角,再畫弧 (arcTo) 就會順帶拉一條線出來...
所以我要先用 moveTo 把起點(diǎn)移到圓形右邊,難道以后畫任何圓都要先來這么一下??我覺得渲染器應(yīng)該幫我做這件事啊喂!簡單分析可以發(fā)現(xiàn),這個(gè)組合動作用協(xié)議現(xiàn)有的方法完全可以做到,我們不必去在原有的 Renderer 協(xié)議中加一個(gè) circleAt 方法,然后在各個(gè)渲染器實(shí)現(xiàn)中逐一實(shí)現(xiàn)一遍這個(gè)重復(fù)的組合操作。
這里我們可以用協(xié)議擴(kuò)展,沒錯(cuò),就是這么寫:

然后圓形類就可以直接調(diào)用渲染器的 circleAt 方法來畫圓了。
最后的代碼:

這就是面向協(xié)議編程了,當(dāng)然還有一部分內(nèi)容本文沒有涉及,我會在以后更新這部分內(nèi)容和"值類型的最佳實(shí)踐"兩篇文章。就是這樣!