Swift编程:抽象类替代与协议应用
1. Swift中抽象类的替代方案
在许多面向对象的编程语言里,我们可以把
Employee
类定义成抽象类,也就是不能被实例化的类,同时将
earnings
属性定义为抽象属性,它没有具体实现,必须由继承自
Employee
的具体类重写。然而,Swift没有抽象类,像
Employee
这样的基类或超类必须为其所有属性和方法提供默认实现。
为了实现类似抽象属性的效果,我们可以使用
fatalError
函数。例如,若要确保
earnings
属性被重写,可将相关代码替换为:
fatalError("Must override the earnings property")
当使用未重写此属性的
Employee
对象或其子类对象时,Swift会终止程序并输出错误信息。
2. Employee子类的实现
2.1 子类
SalariedEmployee
SalariedEmployee
类继承自
Employee
类,并重写了
earnings
和
description
属性。该类包含以下内容:
-
weeklySalary
属性,用于存储周薪。
- 可失败的初始化器,用于初始化继承的
name
属性,验证周薪是否有效,若有效则设置
weeklySalary
属性。
- 重写的计算属性
earnings
,返回
SalariedEmployee
的收入。
- 重写的计算属性
description
,返回包含员工类型、基本信息和周薪的字符串。
// Fig. 10.10: SalariedEmployee.swift
// SalariedEmployee class derived from class Employee
import Foundation
public class SalariedEmployee : Employee {
public var weeklySalary: NSDecimalNumber!
// failable initializer
public init?(name: String, weeklySalary: NSDecimalNumber) {
super.init(name: name) // initialize inherited property
// if any arguments are invalid, return nil
if weeklySalary.compare(NSDecimalNumber.zero()) ==
NSComparisonResult.OrderedAscending {
return nil // an initializer argument was invalid, so fail
}
self.weeklySalary = weeklySalary
}
// earnings computed property
public override var earnings: NSDecimalNumber {
return weeklySalary
}
// description computed property
public override var description: String {
return String(format: "%@: %@\n%@: %@",
"Salaried Employee", super.description,
"Weekly Salary", formatAsCurrency(weeklySalary))
}
}
2.2 子类
CommissionEmployee
CommissionEmployee
类同样继承自
Employee
类,包含以下内容:
-
grossSales
和
commissionRate
属性,分别表示总销售额和佣金率。
- 可失败的初始化器,用于初始化继承的
name
属性,验证总销售额和佣金率是否有效,若有效则设置相应属性。
- 重写的计算属性
earnings
,返回
CommissionEmployee
的收入。
- 重写的计算属性
description
,返回包含员工类型、基本信息、总销售额和佣金率的字符串。
// Fig. 10.11: CommissionEmployee.swift
// CommissionEmployee class derived from Employee
import Foundation
public class CommissionEmployee : Employee {
public var grossSales: NSDecimalNumber!
public var commissionRate: NSDecimalNumber!
// failable initializer
public init?(name: String, grossSales: NSDecimalNumber,
commissionRate: NSDecimalNumber) {
super.init(name: name) // initialize inherited property
// if any arguments are invalid, return nil
if (grossSales.compare(NSDecimalNumber.zero()) ==
NSComparisonResult.OrderedAscending) ||
(commissionRate.compare(NSDecimalNumber.zero()) ==
NSComparisonResult.OrderedAscending) {
return nil // an initializer argument was invalid, so fail
}
self.grossSales = grossSales
self.commissionRate = commissionRate
}
// earnings computed property
public override var earnings: NSDecimalNumber {
return commissionRate.decimalNumberByMultiplyingBy(grossSales)
}
// description computed property
public override var description: String {
return String(format:"%@: %@\n%@: %@\n%@: %@",
"Commission Employee", super.description,
"Gross Sales", formatAsCurrency(grossSales),
"Commission Rate", formatAsPercent(commissionRate))
}
}
2.3 间接子类
BasePlusCommissionEmployee
BasePlusCommissionEmployee
类继承自
CommissionEmployee
类,也是
Employee
的间接子类,包含以下内容:
-
baseSalary
属性,用于存储基本工资。
- 可失败的初始化器,用于调用超类
CommissionEmployee
的初始化器,验证基本工资是否有效,若有效则设置
baseSalary
属性。
- 重写的计算属性
earnings
,通过调用超类的
earnings
属性获取佣金部分收入,再加上基本工资得到总收入。
- 重写的计算属性
description
,返回包含“Base - Salaried”、超类描述信息和基本工资的字符串。
// Fig. 10.12: BasePlusCommissionEmployee.swift
// BasePlusCommissionEmployee class derived from CommissionEmployee
import Foundation
public class BasePlusCommissionEmployee : CommissionEmployee {
public var baseSalary: NSDecimalNumber!
// failable initializer
public init?(name: String, grossSales: NSDecimalNumber,
commissionRate: NSDecimalNumber, baseSalary: NSDecimalNumber)
{
super.init(name: name, grossSales: grossSales,
commissionRate: commissionRate)
// validate baseSalary
if baseSalary.compare(NSDecimalNumber.zero()) ==
NSComparisonResult.OrderedAscending {
return nil // baseSalary was invalid, so fail
}
self.baseSalary = baseSalary
}
// earnings computed property
public override var earnings: NSDecimalNumber {
return baseSalary.decimalNumberByAdding(super.earnings)
}
// description computed property
public override var description: String {
return String(format: "%@ %@\n%@: %@",
"Base-Salaried", super.description, "Base Salary",
formatAsCurrency(baseSalary))
}
}
3. 多态处理
为了测试
Employee
层次结构,我们创建了
SalariedEmployee
、
CommissionEmployee
和
BasePlusCommissionEmployee
的对象。程序先通过各自类型的变量操作这些对象,然后使用
Employee
基类引用的数组进行多态处理。
// Fig. 10.13: main.swift
// Polymorphic Employee hierarchy
import Foundation
// create a SalariedEmployee
let salariedEmployee = SalariedEmployee(name: "John Smith",
weeklySalary: NSDecimalNumber(string: "800.00"))!
// create a CommissionEmployee
let commissionEmployee =
CommissionEmployee(name: "Sue Jones",
grossSales:NSDecimalNumber(string: "10000.00"),
commissionRate: NSDecimalNumber(string: "0.06"))!
// create and test a BasePlusCommissionEmployee
let basePlusCommissionEmployee =
BasePlusCommissionEmployee(name: "Bob Lewis",
grossSales:NSDecimalNumber(string: "5000.00" ),
commissionRate: NSDecimalNumber(string: "0.04"),
baseSalary: NSDecimalNumber(string: "300.0"))!
println("EMPLOYEES PROCESSED INDIVIDUALLY\n")
print("\(salariedEmployee.description)\nEarned: ")
println(formatAsCurrency(salariedEmployee.earnings))
print("\n\(commissionEmployee.description)\nEarned: ")
println(formatAsCurrency(commissionEmployee.earnings))
print("\n\(basePlusCommissionEmployee.description)\nEarned: ")
println(formatAsCurrency(basePlusCommissionEmployee.earnings))
println()
// create initially empty Array of Employees
var employees: [Employee] = []
// initialize array with Employees
employees.append(salariedEmployee)
employees.append(commissionEmployee)
employees.append(basePlusCommissionEmployee)
println("\nEMPLOYEES PROCESSED POLYMORPHICALLY\n")
// display each Employee’s description and earnings properties
for currentEmployee in employees {
println(currentEmployee.description)
// if BasePlusCommissionEmployee, increase base salary
if let employee = currentEmployee as? BasePlusCommissionEmployee {
employee.baseSalary =
employee.baseSalary.decimalNumberByMultiplyingBy(
NSDecimalNumber(string: "1.1"))
print("New base salary with 10% increase is: ")
println(formatAsCurrency(employee.baseSalary))
}
println("Earned: \(formatAsCurrency(currentEmployee.earnings))\n")
}
在多态处理过程中,我们通过
for
循环遍历
employees
数组,使用
currentEmployee
变量获取每个员工的描述和收入信息。对于
BasePlusCommissionEmployee
对象,我们使用
as?
向下转型操作符判断对象类型,若为该类型则将其基本工资提高10%。
4. Swift协议的介绍
协议描述了可以在对象上调用的一组功能,但不提供这些功能的具体实现。协议可以描述属性、方法、初始化器、下标和操作符等,这些被称为协议的要求。协议类似于其他面向对象编程语言中的接口,类、结构体和枚举都可以采用任意数量的协议并实现其功能,这个过程称为符合协议。
4.1 协议能力的实现要求
在Objective - C中,采用一个协议并只实现其部分方法是常见的做法。但在Swift中,默认情况下,采用协议的每个类型都必须实现协议中定义的所有功能,否则会出现编译错误。不过,我们可以使用
@objc
和
optional
关键字创建不需要全部实现的协议成员。
4.2 协议与is - a关系
当一个类型符合某个协议后,该类型的所有对象都与协议类型存在“is - a”关系,并且保证提供协议描述的功能。因此,所有符合给定协议的类型都可以进行多态处理。在继承层次结构中,如果超类符合某个协议,那么它的所有子类也符合该协议。
4.3 协议关联不同类型
协议对于为不同类型分配共同功能非常有用,这使得符合协议的类、结构体和枚举类型的对象可以进行多态处理。例如,符合
Printable
协议的任何类型的对象都可以通过字符串插值转换为字符串,或者使用
print
或
println
函数显示为字符串。
5. 应付账款应用案例
为了演示协议的创建和使用,我们对之前的工资应用进行修改。假设公司希望在应付账款应用中执行一些会计操作,除了计算每个员工的收入,还需要计算几张发票的应付金额。虽然这两个操作应用于不同的事物(员工和发票),但都与确定某种支付金额有关。
5.1 开发可支付层次结构
为了构建一个能同时计算员工和发票支付金额的应用,我们首先创建
Payable
协议,该协议包含一个
paymentAmount
属性,用于返回应付金额。然后引入
Invoice
类,该类采用
Payable
协议并实现
paymentAmount
属性。接着,通过扩展为
Employee
层次结构添加
Payable
协议的符合,使所有
Employee
子类也能成为可支付的对象。同时,让
Invoice
和
Employee
对象符合
Printable
协议,这样
print
和
println
函数可以隐式访问对象的
description
属性来显示对象的字符串表示。
// Fig. 10.15: Payable.swift
// Payable protocol definition
import Foundation
protocol Payable {
var paymentAmount: NSDecimalNumber {get}
}
在协议中,属性总是以
var
关键字开头,类型属性以
class var
开头。协议中的实例属性可以是只读的(使用
{get}
)或读写的(使用
{get set}
)。符合协议的类型可以将只读属性定义为常量或变量,而读写属性必须定义为变量,同时可以选择将属性实现为存储属性或计算属性。
总结
通过上述内容,我们了解了Swift中如何通过
fatalError
函数模拟抽象类的部分功能,以及如何使用子类和多态处理来实现复杂的对象层次结构。同时,深入探讨了协议的概念、使用方法和应用场景,协议为不同类型的对象提供了一种统一的处理方式,增强了代码的灵活性和可扩展性。在实际开发中,我们可以根据具体需求合理运用这些特性,构建高效、可维护的Swift应用程序。
以下是一个简单的mermaid流程图,展示了
Employee
层次结构和多态处理的流程:
graph TD;
A[Employee] --> B[SalariedEmployee];
A --> C[CommissionEmployee];
C --> D[BasePlusCommissionEmployee];
E[创建对象] --> F[各自类型变量操作];
F --> G[多态处理];
G --> H[获取描述和收入信息];
H --> I{是否为BasePlusCommissionEmployee};
I -- 是 --> J[提高基本工资];
I -- 否 --> K[继续循环];
表格展示各子类的关键属性和方法:
| 子类名称 | 关键属性 | 重写方法 |
| ---- | ---- | ---- |
|
SalariedEmployee
|
weeklySalary
|
earnings
,
description
|
|
CommissionEmployee
|
grossSales
,
commissionRate
|
earnings
,
description
|
|
BasePlusCommissionEmployee
|
baseSalary
|
earnings
,
description
|
Swift编程:抽象类替代与协议应用
6. 协议在应付账款应用中的详细实现
6.1 定义
Payable
协议
协议
Payable
是整个应付账款应用的核心,它规定了所有可支付对象必须具备的能力,即提供一个
paymentAmount
属性来确定应付金额。以下是
Payable
协议的定义:
// Fig. 10.15: Payable.swift
// Payable protocol definition
import Foundation
protocol Payable {
var paymentAmount: NSDecimalNumber {get}
}
在这个协议中,
paymentAmount
是一个只读的计算属性,它返回一个
NSDecimalNumber
类型的值,表示应付金额。协议只规定了属性的类型和读写权限,具体的实现由符合该协议的类型负责。
6.2 创建
Invoice
类并实现
Payable
协议
Invoice
类代表了公司的发票,它需要实现
Payable
协议的
paymentAmount
属性。以下是
Invoice
类的示例代码:
// Invoice.swift
import Foundation
class Invoice: Payable {
// 假设发票有商品数量、单价等属性
var quantity: Int
var pricePerItem: NSDecimalNumber
init(quantity: Int, pricePerItem: NSDecimalNumber) {
self.quantity = quantity
self.pricePerItem = pricePerItem
}
// 实现Payable协议的paymentAmount属性
var paymentAmount: NSDecimalNumber {
return pricePerItem.decimalNumberByMultiplyingBy(NSDecimalNumber(integer: quantity))
}
// 实现Printable协议的description属性
var description: String {
return "Invoice: Quantity - \(quantity), Price per item - \(formatAsCurrency(pricePerItem)), Total - \(formatAsCurrency(paymentAmount))"
}
}
在
Invoice
类中,我们定义了
quantity
和
pricePerItem
属性来表示发票的商品数量和单价。
paymentAmount
属性通过计算商品数量和单价的乘积来确定发票的总金额。同时,为了方便打印发票信息,我们还实现了
Printable
协议的
description
属性。
6.3 为
Employee
层次结构添加
Payable
协议支持
通过扩展的方式,我们可以为
Employee
层次结构添加
Payable
协议的支持,使所有
Employee
子类都成为可支付的对象。以下是扩展代码:
// Employee+Payable.swift
import Foundation
extension Employee: Payable {
var paymentAmount: NSDecimalNumber {
return earnings
}
}
在这个扩展中,我们为
Employee
类添加了
Payable
协议的实现。由于员工的应付金额就是其收入,所以
paymentAmount
属性直接返回
earnings
属性的值。
7. 多态处理应付账款
有了
Payable
协议和实现该协议的
Invoice
类以及
Employee
层次结构,我们可以进行多态处理,统一计算员工和发票的应付金额。以下是一个示例代码:
// main.swift
import Foundation
// 创建员工对象
let salariedEmployee = SalariedEmployee(name: "John Smith",
weeklySalary: NSDecimalNumber(string: "800.00"))!
let commissionEmployee =
CommissionEmployee(name: "Sue Jones",
grossSales:NSDecimalNumber(string: "10000.00"),
commissionRate: NSDecimalNumber(string: "0.06"))!
let basePlusCommissionEmployee =
BasePlusCommissionEmployee(name: "Bob Lewis",
grossSales:NSDecimalNumber(string: "5000.00" ),
commissionRate: NSDecimalNumber(string: "0.04"),
baseSalary: NSDecimalNumber(string: "300.0"))!
// 创建发票对象
let invoice = Invoice(quantity: 5, pricePerItem: NSDecimalNumber(string: "200.00"))
// 创建可支付对象数组
var payables: [Payable] = [salariedEmployee, commissionEmployee, basePlusCommissionEmployee, invoice]
// 多态处理可支付对象
println("PAYABLES PROCESSED POLYMORPHICALLY\n")
for payable in payables {
println(payable.description)
println("Payment Amount: \(formatAsCurrency(payable.paymentAmount))\n")
}
在这个示例中,我们创建了员工对象和发票对象,并将它们添加到一个
Payable
类型的数组中。通过遍历这个数组,我们可以统一调用每个对象的
description
和
paymentAmount
属性,实现了多态处理。
8. 类型转换和协议检查
在多态处理过程中,有时我们需要进行类型转换和协议检查,以执行特定类型的操作。例如,我们可能需要检查一个
Payable
对象是否是
Employee
类型,并对其进行特殊处理。以下是一个示例代码:
// 继续上面的代码
for payable in payables {
if let employee = payable as? Employee {
// 如果是员工对象,进行特殊处理
println("This is an employee: \(employee.name)")
if let basePlusEmployee = employee as? BasePlusCommissionEmployee {
// 如果是BasePlusCommissionEmployee对象,提高基本工资
basePlusEmployee.baseSalary =
basePlusEmployee.baseSalary.decimalNumberByMultiplyingBy(
NSDecimalNumber(string: "1.1"))
println("New base salary with 10% increase is: \(formatAsCurrency(basePlusEmployee.baseSalary))")
}
} else if let invoice = payable as? Invoice {
// 如果是发票对象,进行特殊处理
println("This is an invoice: \(invoice.description)")
}
println("Payment Amount: \(formatAsCurrency(payable.paymentAmount))\n")
}
在这个示例中,我们使用
as?
操作符进行类型转换和协议检查。如果
Payable
对象是
Employee
类型,我们可以进一步检查是否是
BasePlusCommissionEmployee
类型,并对其基本工资进行调整。如果是
Invoice
类型,我们可以进行发票相关的特殊处理。
9. 总结与最佳实践
通过以上的介绍,我们深入了解了Swift中抽象类的替代方案、子类的实现、多态处理以及协议的使用。以下是一些总结和最佳实践:
-
抽象类替代
:在Swift中,虽然没有抽象类,但可以使用
fatalError
函数确保属性或方法被重写,模拟抽象类的部分功能。
-
子类实现
:通过继承和重写属性、方法,我们可以创建复杂的对象层次结构,实现不同类型的功能。
-
多态处理
:利用基类引用和协议,我们可以实现多态处理,统一处理不同类型的对象,提高代码的灵活性和可扩展性。
-
协议使用
:协议为不同类型的对象提供了一种统一的处理方式,使代码更加模块化和可维护。在设计协议时,应尽量保持协议的简洁性和单一职责原则。
以下是一个mermaid流程图,展示了应付账款应用的多态处理流程:
graph TD;
A[创建Payable对象] --> B[添加到Payable数组];
B --> C[遍历数组];
C --> D{对象类型};
D -- Employee --> E[员工特殊处理];
D -- Invoice --> F[发票特殊处理];
E --> G[计算支付金额];
F --> G;
G --> H[输出信息];
表格展示不同类型对象的处理方式:
| 对象类型 | 处理方式 |
| ---- | ---- |
|
Employee
| 可能进行基本工资调整等特殊处理,支付金额为收入 |
|
Invoice
| 计算商品总价作为支付金额 |
通过合理运用这些技术,我们可以构建出高效、可维护的Swift应用程序。在实际开发中,应根据具体需求选择合适的技术方案,不断优化代码结构和性能。
超级会员免费看
11

被折叠的 条评论
为什么被折叠?



