第四周第一天
协议,协议的语法
protocol Flyable {
func fly()
}
protocol Fightable {
func fight()
}
协议是方法的集合(计算属性相当于就是方法)
// 可以把看似不相关的对象的公共行为放到一个协议中
// 协议在Swift开发中大致有三种作用:
// 1. 能力 - 遵循了协议就意味着具备了某种能力
// 2. 约定 - 遵循了协议就一定要实现协议中的方法
// 3. 角色 - 一个类可以遵循多个协议, 一个协议可以被多个类遵循, 遵循协议就意味着扮演了某种角色, 遵循多个协议就意味着可以扮演多种角色
// Swift中的继承是单一继承(一个类只能有一个父类), 如果希望让一个类具备多重能力可以使用协议来实现(C++里面是通过多重继承来实现的, 这是一种非常狗血的做法)
协议的继承
class Boxer: Fightable {
@objc func fight() {
print("正在进行格斗.")
}
}
class Bird: Flyable {
func fly() {
print("鸟儿扇动翅膀飞行.")
}
}
class Airplane: Flyable {
func fly() {
print("飞机依靠空气动力学原理飞行.")
}
}
协议的拓展也可以说协议的默认实现当在类中没有实现这个方法默认实现的方法
协议扩展 - 可以在协议扩展中给协议中的方法提供默认实现
也就是说如果某个类遵循了协议但是没有实现这个方法就直接使用默认实现
那么这个方法也就相当于是一个可选方法(可以实现也可以不实现)
extension Fightable {
func fight() {
print("正在打架")
}
}
依赖倒转原则(面向协议编程)
- 声明变量的类型时应该尽可能使用协议类型
- 声明方法参数类型时应该尽可能使用协议类型
- 声明方法返回类型时应该尽可能使用协议类型
协议的组合
let array: [protocol<Flyable, Fightable>] = [
// Rocket(),
// Bird(),
Superman(),
// Boxer()
// Airplane()
]
//只有同时满足两个协议的类才可以
for obj in array {
obj.fly()
obj.fight()
}
协议中全是抽象概念(只有声明没有实现) 遵循协议的类可以各自对协议中的计算属性和方法给出自己的实现版本 这样当我们面向协议编程时就可以把多态的优势发挥到淋漓尽致 可以写出更通用更灵活的代码(符合开闭原则)
实现开闭原则最关键有两点:
- 抽象是关键(在设计系统的时候一定要设计好的协议);
- 封装可变性(桥梁模式 - 将不同的可变因素封装到不同的继承结构中)
接口(协议)隔离原则: 协议的设计要小而专不要大而全
协议的设计也要高度内聚
protocol Shape {
var perimeter: Double { get }
var area: Double { get }
func draw()
}
class Triangle: Shape {
var a: Double
var b: Double
var c: Double
init(a: Double, b: Double, c: Double) {
assert(a + b > c && b + c > a && a + c > b, "不能构成三角形")
self.a = a
self.b = b
self.c = c
}
var perimeter: Double {
get { return a + b + c }
}
var area: Double {
get {
let half = perimeter / 2
return sqrt(half * (half - a) * (half - b) * (half - c))
}
}
func draw() {
print("△")
}
}
协议的应用
#### //图书类的定义
import Foundation
/// 图书
class Book {
var name: String
var price: Double
var type: String
// 四人帮设计模式 - 策略模式
var strategy: DiscountStrategy?
/**
初始化方法
- parameter name: 书名
- parameter price: 价格
- parameter type: 类型
*/
init(name: String, price: Double, type: String) {
self.name = name
self.price = price
self.type = type
}
/// 减多少钱
var discountValue: Double {
get {
if let s = strategy {
return s.discount(price)
}
else {
return 0
}
}
}
/// 折后价格
var discountedPrice: Double {
get { return price - discountValue }
}
}
#### //写协议部分
import Foundation
/**
* 打折策略协议
*/
protocol DiscountStrategy {
/**
计算折扣
- parameter price: 原价
- returns: 折扣的金额
*/
func discount(price: Double) -> Double
}
/// 百分比折扣策略
class PercentageDiscount: DiscountStrategy {
var percentage: Double
init(percentage: Double) {
self.percentage = percentage
}
func discount(price: Double) -> Double {
return price * (1 - percentage)
}
}
// 固定金额折扣策略
class FixedDiscount: DiscountStrategy {
var fixedMoney: Double
init(fixedMoney: Double) {
self.fixedMoney = fixedMoney
}
func discount(price: Double) -> Double {
return price >= fixedMoney ? fixedMoney : 0
}
}
// 分段折后策略
class SegmentedDiscount: DiscountStrategy {
func discount(price: Double) -> Double {
if price < 20 {
return 0
}
else if price < 50 {
return 3
}
else if price < 100 {
return 10
}
else {
return 30
}
}
}
#### //实现和调用部分
import Foundation
let booksArray = [
Book(name: "C语言程序设计", price: 24.0, type: "计算机"),
Book(name: "名侦探柯南", price: 98.5, type: "漫画"),
Book(name: "Swift从入门到住院", price: 35.8, type: "计算机"),
Book(name: "黄冈数学密卷", price: 34.2, type: "教材"),
Book(name: "中国股市探秘", price: 58.5, type: "金融")
]
let discountDict: [String: DiscountStrategy] = [
"计算机": PercentageDiscount(percentage: 0.78),
"教材": PercentageDiscount(percentage: 0.85),
"漫画": SegmentedDiscount(),
"科普": FixedDiscount(fixedMoney: 2)
]
var totalPrice = 0.0
var totalDiscount = 0.0
for book in booksArray {
if let strategy = discountDict[book.type] {
book.strategy = strategy
}
print("《\(book.name)》原价: ¥\(book.price)元")
print("《\(book.name)》折后价: ¥\(book.discountedPrice)元")
totalPrice += book.discountedPrice
totalDiscount += book.discountValue
}
print(String(format: "总计: ¥%.1f元", totalPrice))
print(String(format: "为您节省了: ¥%.1f元", totalDiscount))