官方文档Protocol-Oriented Programming in Swift
Swift设计的核心是两个非常强大的理念:面向协议的编程和一流的价值语义。这些概念中的每一个都有益于可预测性、性能和生产力,它们一起可以改变我们对编程的思考方式。了解如何应用这些思想来改进编写的代码。
承前启后
class的弊端
- Implicit Sharing
- 重点隐士共享的坏处
-
Inheritance All Up In Your Business
- 重点单向继承,继承负担,不能反向建模
-
Lost Type Relationships
- 图示圈出的代码部分类型推断差,不优雅.(后续与协议方式作比较)
更好的抽象机制
进化
面向协议
协议基础示例
基本协议
protocol Ordered {
func precedes(other: Self) -> Bool
}
struct Number : Ordered {
var value: Double = 0
func precedes(other: Number) -> Bool {
return self.value < other.value
}
}
泛型约束
func binarySearch<T: Ordered>(sortedKeys: [T], forKey k: T) -> Int {
var lo = 0
var hi = sortedKeys.count
while hi > lo {
let mid = lo + (hi - lo) / 2
if sortedKeys[mid].precedes(other: k) {
lo = mid + 1
} else {
hi = mid
}
}
return lo
}
好处:后面为协议
绘图
图形统一遵循Drawable协议, Drawable协议绘图方法内包含一个渲染器,此渲染器完成绘制工作.
图形Drawable协议
protocol Drawable {
func draw(renderer: Renderer)
}
struct Circle : Drawable {
var center: CGPoint
var radius: CGFloat
func draw(renderer: Renderer) {
renderer.arcAt(center: center, radius: radius, startAngle: 0.0, endAngle: CGFloat.pi * 2)
}
}
struct Polygon : Drawable {
var corners: [CGPoint] = []
func draw(renderer: Renderer) {
renderer.moveTo(p: corners.last!)
for p in corners {
renderer.lineTo(p: p)
}
}
}
struct Diagram : Drawable {
var elements: [Drawable] = []
func draw(renderer: Renderer) {
for f in elements {
f.draw(renderer: renderer)
}
}
}
Renderer协议
- extension CGContext :Renderer, 扩展CGContext继承Renderer协议, 于是CGContext完成绘制工作.
protocol Renderer {
func moveTo(p: CGPoint)
func lineTo(p: CGPoint)
func circleAt(center: CGPoint, radius: CGFloat)
func arcAt(
center: CGPoint, radius: CGFloat, startAngle: CGFloat, endAngle: CGFloat)
}
extension Renderer {
func circleAt(center: CGPoint, radius: CGFloat){
arcAt(center: center, radius: radius, startAngle: 0, endAngle: CGFloat.pi * 2)
}
}
extension CGContext :Renderer {
func moveTo(p: CGPoint) {
move(to: p)
}
func lineTo(p: CGPoint) {
addLine(to: p)
}
func arcAt(center: CGPoint, radius: CGFloat,
startAngle: CGFloat, endAngle: CGFloat) {
addArc(center: center, radius: radius, startAngle: startAngle, endAngle: endAngle, clockwise: true)
}
}
使用
override func draw(_ rect: CGRect) {
super.draw(rect)
guard let contex = UIGraphicsGetCurrentContext() else {
return
}
let circle = Circle(center: CGPoint(x: 187.5, y: 333.5), radius: 93.75)
let triangle = Polygon(corners: [ CGPoint(x: 187.5, y: 427.25), CGPoint(x: 268.69, y: 286.625), CGPoint(x: 106.31, y: 286.625)])
let diagram = Diagram(elements: [circle, triangle])
contex.setLineWidth(2)
contex.setStrokeColor(UIColor.orange.cgColor)
diagram.draw(renderer: contex)
contex.closePath()
contex.strokePath()
}
协议扩展技巧
Scenes from the standard library and beyond
约束限制1
extension Collection where Iterator.Element: Equatable {
public func indexOf(element: Iterator.Element) -> Index? {
for i in self.indices {
if self[i] == element {
return i
}
}
return nil
}
}
约束限制2
protocol Ordered {
func precedes(other: Self) -> Bool
}
extension Ordered where Self : Comparable {
func precedes(other: Self) -> Bool {
return self < other
}
}
泛型美化
美化前
func binarySearch<T: Ordered>(sortedKeys: [T], forKey k: T) -> Int {
var lo = 0
var hi = sortedKeys.count
while hi > lo {
let mid = lo + (hi - lo) / 2
if sortedKeys[mid].precedes(other: k) {
lo = mid + 1
} else {
hi = mid
}
}
return lo
}
//代码已不适用现在使用
func binarySearch<
C : Collection>(sortedKeys: C, forKey k: C.Iterator.Element) -> Int where C.Index == RandomAccessIndexType, C.Iterator.Element : Ordered
{
var lo = 0
var hi = sortedKeys.count
while hi > lo {
let mid = lo + (hi - lo) / 2
if sortedKeys[mid].precedes(other: k) {
lo = mid + 1
} else {
hi = mid
}
}
return lo
}