WWDC 2015 - Session 408 - iOS, OS X, watchOS
本文核心基于 WWDC 主题演讲内容,结合我自己的一些想法,大家可以当成翻译看。
Back to then
假设我们要设计一个画板应用,核心的模块应该是可绘制对象和渲染引擎,我分别称它们为Drawable
, Renderer
。我们知道所有可绘制对象都应该有一个方法来执行绘制操作,也就是说不管矩形、圆形还是其他图形都应该有一个可以统一调用的 render
,于是很容易想到的就是声明一个抽象类,里面有一个未实现的 render
函数,但是这里我并不想使用 class
,而是使用 struct
,为什么呢?因为图形都是由基本值类型构成的,而且功能比较单一。有关于引用类型和值类型的最佳实践我会再开一篇文章来写(同样也是 WWDC 的内容)。
首先我们写一个矩形类型:
下面我们设计渲染类,为了方便测试图形绘图的行为,我们先写一个“假的”渲染器,来向控制台输出调试语句:
但是我会说,这是个很差的设计,它很难被重构、扩展、推广。如果我想用 CoreGraphics 渲染怎么办?用 QuartzCore 呢?以后推广到 X Server 呢(虽然有点扯)?逐一重写?
Now, let's Protocol-Oriented Programming
回到标题,还是用协议,统一所有的接口方法:
那么上面的测试渲染器就可以遵守这个协议来写实现了:
下面介绍一下 extension 在协议方面的应用,现在我们来写基于 CoreGraphics 的实现,也就是实现一个渲染器,它可以操作 CGContext
来画图,继承 CGContext
?为什么不扩展它呢?
通过 extension
关键字我们可以将一个现有的类扩展到遵守一个协议。随手写一个 UIView
来显示绘制内容,这里就不解释了。
下面测试一下吧!
Hold on
下面我们再写一个圆形类和组合类吧。组合类没什么好说的,说说圆形类。
圆形类型的定义我这么写的:
然后看看结果:
诶,什么鬼?怎么有条线啊....
因为 lineTo 完毕后起点在矩形左上角,再画弧 (arcTo) 就会顺带拉一条线出来...
所以我要先用 moveTo 把起点移到圆形右边,难道以后画任何圆都要先来这么一下??我觉得渲染器应该帮我做这件事啊喂!简单分析可以发现,这个组合动作用协议现有的方法完全可以做到,我们不必去在原有的 Renderer
协议中加一个 circleAt
方法,然后在各个渲染器实现中逐一实现一遍这个重复的组合操作。
这里我们可以用协议扩展,没错,就是这么写:
然后圆形类就可以直接调用渲染器的 circleAt
方法来画圆了。
最后的代码:
这就是面向协议编程了,当然还有一部分内容本文没有涉及,我会在以后更新这部分内容和"值类型的最佳实践"两篇文章。就是这样!