SOLID 介绍

面向对象编程(OOP)是一种程序规划范式,它运用“对象”来规划软件。对象能够包括数据(称为属性或字段)和操作数据的代码(称为办法)。SOLID 准则是面向对象规划和编程中的一组五个基本准则,它们有助于软件开发者规划易于办理和扩展的体系。这些准则在 Swift 编程言语中同样适用,如下所述:

  1. 单一责任准则(Single Responsibility Principle, SRP) :一个类应该只要一个引起它变化的原因。这意味着一个类应该只担任一件事情。在 Swift 中,你能够经过将功用拆分为多个专心于单一使命的类或结构体来完成这一点。
  2. 开放关闭准则(Open/Closed Principle, OCP) :软件实体(类、模块、函数等)应该对扩展开放,对修正关闭。这意味着应该能够在不修正现有代码的情况下扩展一个类的功用。在 Swift 中,能够经过运用协议和扩展来完成,答应现有类型获得新功用而无需改动其源代码
  3. 里氏替换准则(Liskov Substitution Principle, LSP) :子类型必须能够替换掉它们的基类型。这意味着假如类 A 是类 B 的子类,则应该能够在不改动程序期望成果的情况下,用类 B 替换类 A 的实例。在 Swift 中,遵从这一准则意味着子类应该恪守父类的契约,同时不引进损坏性行为。
  4. 接口阻隔准则(Interface Segregation Principle, ISP) :不应该逼迫客户端依靠于它们不运用的接口。在 Swift 中,这意味着应该避免创立大而全的协议,而是应该将它们分解为更小的、更专用的协议,使得完成类只需关注它们真正需求的部分。
  5. 依靠倒置准则(Dependency Inversion Principle, DIP) :高层模块不应该依靠于低层模块,它们都应该依靠于笼统;笼统不应该依靠于细节,细节应该依靠于笼统。在 Swift 中,这通常意味着依靠于协议而不是具体的类,使得能够灵活地替换具体完成,而不会影响到高层模块的规划。

遵从这些 SOLID 准则能够帮助 Swift 开发者规划出更灵活、更简单保护和扩展的应用程序。

单一责任

A class should have just a unique reason to be changed, or in other words, a class should have a single responsibility.

单一责任即: 一个类改动的原因只要一个,也便是类的责任是单一的。

但什么是责任,以及怎么判断类只要一个责任呢?责任能够认为是类担任履行的人物,也能够认为是类改动的原因。假如有多个原因要改动类,类就承担了多个责任。

以新能源轿车产品的开发迭代为例。

轿车的迭代

第一代: 以驾驭功用为主。包括:加快 , 刹车。

class Car {
 // 加快
 func accelerate() { }
 // 刹车
 func brake() { }
}

第二代:以轿车的保养为主。 包括:充电。

extension Car {
 /// 充电
 func addCharge()
}

第三代:以轿车的文娱为主。包括:播映音乐。

extension Car {
 /// 播映CD
 func playCD()
 
 /// AC操控
 func turnOnAC()
}

随着业务的不断迭代,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()
   }
 }
 
 func turnOnAC() {
   if battery.hasCharge() {
     ac.turnOn()
   }
 }
}

电源对于多个功用都是必不可少的。accelerate()addCharge()playCD()turnOnAC()办法都需求电源,对一个办法的修正可能影响其它办法。

驾驭的优先级高于文娱,电量低时能够禁用文娱功用。如下:

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

耦合的责任越多,就越简单出现问题。对行进功用的修正,可能影响到保养、文娱功用。这些副作用是危险的,会损坏程序的稳定性。

用协议拆分功用

根据轿车的功用,拆分为三个协议:驾驭操控,轿车保养,轿车文娱。

/// 驾驭操控
protocol Drivable {
 // 加快
 func accelerate()
 // 刹车
 func brake()
}
​
/// 保养
protocol Maintainable {
 /// 充电
 func addCharge()
}
​
/// 文娱
protocol Comfortable {
 /// 播映CD
 func playCD()
 /// AC操控
 func turnOnAC()
}

看似经过协议将责任进行了拆分,实则否则:

class Car: Drivable, Maintainable, Comfortable {
 func accelerate() { }
 func brake() { }
 func addCharge() { }
 func playCD() { }
 func turnOnAC() { }
}

这样就又康复到了开始的版别,Car完成了协议的一切办法。

运用代理解决问题

将驾驭,保养,文娱责任代理给 Driving, Maintenance, Comfort

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: PlayCDable, TurnOnACable {
 private var battery = Battery()
 init(battery: Battery) {
   self.battery = battery
 }
 
 func playCD() {
   guard battery.hasCharge() else { return }
   // 播映音乐
   battery.freeCharge(quantity: 1)
 }
 
 func turnOnAC() {
   guard battery.hasCharge() else { return }
   // AC敞开
   battery.freeCharge(quantity: 3)
 }
}

Car类担任安排和和谐各个组件。

class Car {
 
 var 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() }
 
 func turnOnAC() { comfort.turnOnAC() }
}

在这种规划中:

  • Car类担任安排和和谐各个组件(Driving, Maintenance, ComfortBattery),它不直接参与具体的功用完成,只担任通用的电池办理和各组件之间的和谐。
  • Driving, Maintenance, Comfort这些组件各自担任一项或一组相关的责任,无论是驾驭功用、保养相关功用还是舒适性功用,它们都在各自的领域内坚持独立和封装。
  • Battery作为一个独立的组件,它的责任便是办理电量相关的行为,包括充电和放电。它的状态能够由Car类根据需求进行办理,可是具体的行为和完成都封装在Battery类中。这样battery只能经过暴露的接口修正。

这种规划下,每个类或许组件都有明确的责任,当某个功用需求修正或许扩展时,只需求对应的类或组件进行修正,不会影响到其他的组件,这便是单一责任准则的长处。