Skip to content

Commit

Permalink
单一职责原则
Browse files Browse the repository at this point in the history
  • Loading branch information
pro648 committed Jun 18, 2022
1 parent 3a87701 commit 86cf136
Show file tree
Hide file tree
Showing 2 changed files with 250 additions and 0 deletions.
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,10 @@ Demo下载点击[BasicDemos-iOS](https://github.com/pro648/BasicDemos-iOS)
- [迭代器模式 Iterator Pattern](https://github.com/pro648/tips/blob/master/sources/%E8%BF%AD%E4%BB%A3%E5%99%A8%E6%A8%A1%E5%BC%8F%20Iterator%20Pattern.md)
- [适配器模式 Adapter Pattern](https://github.com/pro648/tips/blob/master/sources/%E9%80%82%E9%85%8D%E5%99%A8%E6%A8%A1%E5%BC%8F%20Adapter%20Pattern.md)

### SOLID原则

- [单一职责原则](https://github.com/pro648/tips/blob/master/sources/%E5%8D%95%E4%B8%80%E8%81%8C%E8%B4%A3%E5%8E%9F%E5%88%99.md)


### 工具

Expand Down
246 changes: 246 additions & 0 deletions sources/单一职责原则.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,246 @@
这是SOLID五大原则的第一篇学习笔记:单一职责原则 Single Reposibility Principle。

## 1. 什么是SOLID

如果你是一名软件开发人员,并且对软件工程感兴趣,你可能已经听过SOLID设计原则。SOLID是编程设计五大原则首字母缩写,其目的是促进理解和开发,同时增加软件的扩展性和可维护性。

SOLID原则是由Robert C. Martin提出,每个字母对应一种原则。

- S是Single Responsibility Principle的缩写,即单一职责。
- O是Open-Closed Principle的缩写,即开闭原则。
- L是Liskov Substitution Principle的缩写,即里氏替换原则。
- I是Interface Segregation Principle的缩写,即接口隔离原则。
- D是Dependency Inversion Principle的缩写,即依赖反转原则。

在关于SOLID系列文章中,将会介绍每种原则的含义,如何实现,有什么优势,以及不遵守该原则会带来的问题。为了方便理解,介绍过程中会结合demo进行说明。

这篇文章将介绍SOLID原则的第一个:单一职责。

## 2. 单一职责

> 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() { }
func turnLeft() { }
func turnRight() { }
func addFuel() { }
func changeOil() { }
func rotateTires() { }
}
```

这些方法都是汽车的功能,将其放到`Car`类看起来是合理的。

随着业务迭代,类会变成变成:

```
class Car {
...
func rotateTires() { }
func adjustDriverSeat() { }
func turnOnAC() { }
func playCD() { }
}
```

`Car`类违背了单一职责原则,其职责如下:

- 行驶
- 保养
- 娱乐

可以通过将不同职责划分到不同类进行拆分:

```
class DrivableCar {
func accelerate() { }
func brake() { }
func turnLeft() { }
func turnRight() { }
}
class MaintainableCar {
func addFuel() { }
func changeOil() { }
func rotateTires() { }
}
class ComfortableCar {
func adjustDriverSeat() { }
func turnOnAC() { }
func playCD() { }
}
```

这种划分方式会引出一个新的问题,当创建一辆车时,需要创建三个实例`DrivableCar``MaintainableCar``ComfortableCar`

为了解决上述问题,可以通过协议来进行职责划分。

```
protocol Drivable {
func accelerate()
func brake()
func turnLeft()
func turnRight()
}
protocol Maintainable {
func addFuel()
func changeOil()
func rotateTires()
}
protocol Comfortable {
func adjustDriverSeat()
func turnOnAC()
func playCD()
}
class Car: Drivable, Maintainable, Comfortable {
func accelerate() {}
func brake() {}
func turnLeft() {}
func turnRight() {}
func addFuel() {}
func changeOil() {}
func rotateTires() {}
func adjustDriverSeat() {}
func turnOnAC() {}
func playCD() {}
}
```

但这样就又恢复到了最初的版本,`Car`实现了协议的所有方法。

使用`Car`类方式如下:

```
let drivableCar: Drivable
drivableCar = Car()
drivableCar.accelerate()
let maintainableCar: Maintainable
maintainableCar = Car()
maintainableCar.addFuel()
let comfortableCar: Comfortable
comfortableCar = Car()
comfortableCar.playCD()
```

这样就将`Car`的职责划分到了三个接口中,可以防止调用错误。例如,编译器会阻止以下调用`maintainableCar.accelerate()`

## 3. 为什么要职责分离

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

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

如下所示:

```
class Car {
var battery = Battery()
func accelerate() {
if battery.hasCharge() {
moveCar()
}
}
func addFuel() {
battery.charge()
}
func playCD() {
if battery.hasCharge() {
cdPlayer.play()
}
}
}
```

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

驾驶的优先级高于娱乐,电量低时可以禁用娱乐功能。如下:

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

尽管使用protocol隔离了方法,`Car`类仍然实现了所有功能,使用delegate可以解决这一问题。

`Car`类遵守了`Drivable``Maintainable``Comfortable`协议,但具体实现方式是不受限的。其可以将`driving``maintenance``convenience`方法委托其它对象处理。

```
class Driving {
func accelerate() {}
func brake() {}
func turnLeft() {}
func turnRight() {}
}
class Maintenance {
func addFuel() {}
func changeOil() {}
func rotateTires() {}
}
class Convenience {
func adjustDriverSeat() {}
func turnOnAC() {}
func playCD() {}
}
class Car: Drivable, Maintainable, Comfortable {
let driving = Driving()
let maintenance = Maintenance()
let convenience = Convenience()
func accelerate() { driving.accelerate() }
func brake() { driving.brake() }
func turnLeft() { driving.turnLeft() }
func turnRight() { driving.turnRight() }
func addFuel() { maintenance.addFuel() }
func changeOil() { maintenance.changeOil() }
func rotateTires() { maintenance.rotateTires() }
func adjustDriverSeat() { convenience.adjustDriverSeat() }
func turnOnAC() { convenience.turnOnAC() }
func playCD() { convenience.playCD() }
}
```

目前,`Car`类委托`Driving``Maintenance``Convenience`处理相关功能。`Car`类也可以维护`battery`,传递给所需类使用,这样`battery`只能通过暴露的接口修改。

## 总结

尽管单一职责理论上很简单,但不太容易付诸实践。因为有时难以确定哪些算是一种职责,以及何时一个类有了多种职责。通过不断实践,学习好的设计,可以更好的遵守Single Reposibility Principle。

参考资料:

1. [Single Responsibility Principle for Class](https://clean-swift.com/single-responsibility-principle-for-class/)

2. [Single Responsibility Principle in Swift](https://medium.com/movile-tech/single-responsibility-principle-in-swift-61ee11ee81b5)

3. [SRP: Single Responsibility Principle](https://medium.com/swift-india/solid-principles-part-1-single-responsibility-ccfd4ff34a5a)


0 comments on commit 86cf136

Please sign in to comment.