Swift SOLID 1. 单一职责

1,475 阅读5分钟

SOLID 原则简介

SOLID 原则是五个面向对象设计的基本原则,旨在帮助开发者构建易于管理和扩展的系统。具体包括:

  1. 单一职责原则(SRP) :一个类,一个职责。
  2. 开放封闭原则(OCP) :对扩展开放,对修改封闭。
  3. 里氏替换原则(LSP) :子类可替代基类。
  4. 接口隔离原则(ISP) :最小接口,避免不必要依赖。
  5. 依赖倒置原则(DIP) :依赖抽象,不依赖具体。

Swift 编程语言中也适用这些原则,遵循这些原则,Swift 开发者可以设计出更加灵活、易于维护和扩展的应用程序。

单一职责原则

单一职责原则是面向对象设计中的一个重要原则,它强调一个类应该只负责一项职责。换句话说,这意味着一个类应该只有一个改变的理由。这个原则的核心在于促进类的内聚性和减少它们之间的耦合。当你的类遵循单一职责原则时,每个类将专注于执行一个具体的任务,使其更容易理解、测试和维护。

接下来,我们将通过几个示例来深入了解单一职责原则。

示例1:用户设置管理

问题代码

 class UserSettings {
    let user: String
 ​
    init(user: String) {
        self.user = user
    }
 ​
    func changeUserName(newName: String) {
        // 更改用户名
    }
 ​
    func saveUserSettings() {
        // 将用户设置保存到文件中
    }
 }

在这个示例中,UserSettings类不仅负责管理用户的设置,还负责将这些设置保存到某个持久化存储中。这违反了单一职责原则,因为如果保存机制发生变化(例如从文件系统更改为数据库),则需要修改UserSettings类。

优化后的代码

 class User {
    var name: String
 ​
    init(name: String) {
        self.name = name
    }
 ​
    func changeUserName(newName: String) {
        self.name = newName
    }
 }
 ​
 class UserPersistence {
    func saveUser(user: User) {
        // 将用户信息保存到文件中
    }
 }

在这个重构的示例中,将职责分离到两个类中:

  • User类现在只负责用户信息的管理,
  • UserPersistence类负责用户信息的持久化存储。

这样,如果保存机制需要改变,只需修改UserPersistence类,而不影响User类。

示例2:新能源汽车产品开发

公司新项目:新能源汽车产品的开发过程分为三个主要阶段。

第一代: 以驾驶功能为主。包含:加速 , 刹车。

第二代:以汽车的保养为主。 包含:充电。

第三代:以汽车的娱乐为主。包含:播放音乐。

问题代码

 class Car {
    // 加速
    func accelerate() { }
    // 刹车
    func brake() { }
 }
 ​
 extension Car {
    /// 充电
    func addCharge()
 }
 ​
 extension Car {
    /// 播放CD
    func playCD()
 }

随着业务的不断迭代,Car类必然会成为庞大的类。违反了功能设计的单一性原则。

新能源汽车的核心之一是:电池。

 struct Battery {
    private var charges: Int = 0
     
    func hasCharge() -> Bool {
        charges > 0
    }
     
    var isLow: Bool {
        charges < 10
    }
     
    mutating func pushCharge(quantity: Int) {
        charges += quantity
    }
     
    mutating func freeCharge(quantity: Int) {
        charges -= quantity
    }
 }

如果Car类包含了行驶、保养和娱乐职责,即职责混合在一起。修改一个职责时,可能影响另一个职责的内部实现

 class Car {
    var battery = Battery()
     
    func accelerate() {
        if battery.hasCharge() {
            moveCar()
        }
    }
     
    func addCharge() {
        battery.pushCharge(quantity: 60)
    }
     
    func playCD() {
        if battery.hasCharge() {
            cdPlayer.play()
        }
    }
 }

电池对于多个功能都是必不可少的。accelerate()addCharge()playCD()方法都需要电源,对一个方法的修改可能影响其它方法。

驾驶的优先级高于娱乐,电量低时需要禁用娱乐功能。

 func playCD() {
    guard !battery.isLow else { return }
    if battery.hasCharge() {
        cdPlayer.play()
    }
 }

耦合的职责越多,就越容易出现问题。对行驶功能的修改,可能影响到保养、娱乐功能。这些副作用是危险的,会破坏程序的稳定性。

解决方案

为了解决这个问题,我们可以将Car类的职责分解为多个独立的组件,每个组件负责一项或一组相关的职责。

将驾驶,保养,娱乐职责代理给 Driving, Maintenance, Comfort

 // 驾驶控制
 protocol Drivable {
    func accelerate()
    func brake()
 }
 ​
 // 保养
 protocol Maintainable {
    func addCharge()
 }
 ​
 // 播放CD
 protocol Playable {
    func playCD()
 }
 ​
 // 电池管理
 protocol Batteryable {
    func hasCharge() -> Bool
    func freeCharge(quantity: Int)
    func pushCharge(quantity: Int)
 }
 ​
 class Battery: Batteryable {
    private var chargeLevel = 100 // 初始电量百分比
     
    func hasCharge() -> Bool {
        return chargeLevel > 0
    }
     
    func freeCharge(quantity: Int) {
        chargeLevel = max(chargeLevel - quantity, 0)
    }
     
    func pushCharge(quantity: Int) {
        chargeLevel = min(chargeLevel + quantity, 100)
    }
 }
 ​
 class Driving: Drivable {
    private var battery: Battery
     
    init(battery: Battery) {
        self.battery = battery
    }
     
    func accelerate() {
        guard battery.hasCharge() else { return }
        battery.freeCharge(quantity: 10)
    }
     
    func brake() {}
 }
 ​
 class Maintenance: Maintainable {
    private var battery: Battery
     
    init(battery: Battery) {
        self.battery = battery
    }
     
    func addCharge() {
        battery.pushCharge(quantity: 60)
    }
 }
 ​
 class Comfort: Playable {
    private var battery: Battery
     
    init(battery: Battery) {
        self.battery = battery
    }
     
    func playCD() {
        guard battery.hasCharge() else { return }
        battery.freeCharge(quantity: 1)
    }

Car类负责组织和协调各个组件。

 class Car {
    let battery: Battery
    let driving: Driving
    let maintenance: Maintenance
    let comfort: Comfort
     
    init() {
        battery = Battery()
        driving = Driving(battery: battery)
        maintenance = Maintenance(battery: battery)
        comfort = Comfort(battery: battery)
    }
     
    func accelerate() { driving.accelerate() }
    func brake() { driving.brake() }
    func addCharge() { maintenance.addCharge() }
    func playCD() { comfort.playCD() }
 }

在这种设计中:

  • Car类负责组织和协调各个组件(Driving, Maintenance, ComfortBattery),它不直接参与具体的功能实现,只负责通用的电池管理和各组件之间的协调。
  • Driving, Maintenance, Comfort这些组件各自负责一项或一组相关的职责,无论是驾驶功能、保养相关功能还是舒适性功能,它们都在各自的领域内保持独立和封装。
  • Battery作为一个独立的组件,它的职责就是管理电量相关的行为,包括充电和放电。它的状态可以由Car类根据需要进行管理,但是具体的行为和实现都封装在Battery类中。

这种设计下,每个类或者组件都有明确的职责,当某个功能需要修改或者扩展时,只需要对应的类或组件进行修改,不会影响到其他的组件,这就是单一职责原则的优点。