SOLID 原则简介
SOLID 原则是五个面向对象设计的基本原则,旨在帮助开发者构建易于管理和扩展的系统。具体包括:
- 单一职责原则(SRP) :一个类,一个职责。
- 开放封闭原则(OCP) :对扩展开放,对修改封闭。
- 里氏替换原则(LSP) :子类可替代基类。
- 接口隔离原则(ISP) :最小接口,避免不必要依赖。
- 依赖倒置原则(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
,Comfort
和Battery
),它不直接参与具体的功能实现,只负责通用的电池管理和各组件之间的协调。Driving
,Maintenance
,Comfort
这些组件各自负责一项或一组相关的职责,无论是驾驶功能、保养相关功能还是舒适性功能,它们都在各自的领域内保持独立和封装。Battery
作为一个独立的组件,它的职责就是管理电量相关的行为,包括充电和放电。它的状态可以由Car
类根据需要进行管理,但是具体的行为和实现都封装在Battery
类中。
这种设计下,每个类或者组件都有明确的职责,当某个功能需要修改或者扩展时,只需要对应的类或组件进行修改,不会影响到其他的组件,这就是单一职责原则的优点。