26、Swift编程:抽象类替代与协议应用

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应用程序。在实际开发中,应根据具体需求选择合适的技术方案,不断优化代码结构和性能。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值