单一职责原则
定义:单一职责原则的英文名称是Single Responsibility Principle,简称是SRP。SRP原则的解释是:There should never be more than one reason for a class to change。即不要存在多于一个导致类变化的原因,简单来说:就是一个类只负责一项职责。
问题由来:假如一个类T负责两个不同的职责:职责P1、职责P2,如果职责P1需求发生改变而需要修改类T时,有可能会导致原本运行正常的职责P2功能发生故障。也就是说职责P1和P2耦合在一起了。
解决方案:遵循单一职责原则,将不同的职责封装到不同的类或者模块中。分别建立两个类T1、T2,使T1完成职责P1功能,T2完成职责P2功能。这样,当修改类T1时,不会使职责P2发生故障风险;同理,当修改T2时,也不会使职责P1发生故障风险。
单一职责原则告诉我们:
一个类(大到模块,小到方法)承担的职责越多,它被复用的可能性就越小,而且一个类承担的职责过多,就相当于将这些职责耦合在一起,当其中一个职责变化时,可能会影响其他职责的运作,因此要将这些职责进行分离,将不同的职责封装在不同的类中,即将不同的变化原因封装在不同的类中,如果多个职责总是同时发生改变则可将它们封装在同一类中。
职责扩散
所谓职责扩散,就是因为某种原因,职责P被分化为粒度更细的职责P1和P2。比如:类T只负责一个职责P,这样设计是符合单一职责原则的。后来由于某种原因,也许是需求变更了,也许是程序的设计者境界提高了,需要将职责P细分为粒度更细的职责P1,P2,这时如果要使程序遵循单一职责原则,需要将类T也分解为两个类T1和T2,分别负责P1、P2两个职责。但是在程序已经写好的情况下,这样做简直太费时间了。所以,简单的修改类T,用它来负责两个职责是一个比较不错的选择,虽然这样做有悖于单一职责原则。(这样做的风险在于职责扩散的不确定性,因为我们不会想到这个职责P,在未来可能会扩散为P1,P2,P3,P4……Pn。所以记住,在职责扩散到我们无法控制的程度之前,立刻对代码进行重构。)
单一职责优点
1、类的复杂性降低,实现什么职责都有清晰明确的定义
2、可读性提高,当复杂性降低之后,那么可读性自然就提高了
3、可维护性提高,因为可读性提高,当然更容易维护了
4、变更引起的风险降低,变更是必不可少的,接口的单一职责做的好的话,一个接口修改只对相应的实现类有影响
5、保证类的单一职责,类会更加的健壮
如何定义和区分职责
职责的划分每个人的理解都不一样,很难有标准的答案,而且需要根据项目情况而定,我们需要做的是让一个类的职责尽可能的清楚,保证接口职责单一,类的职责尽可能单一,比如:类方法/函数遵循单一职责,只关心输入、输出和职责,不参杂额外的信息。但是实际的开发中有些情况很难真正做到SRP,所以在实际的开发过程中要学会变通,本来一个类完全可以实现就没变化一定要拆分为两个类来实现,这样使用聚合或者组合的方式,会增加耦合性和系统的复杂性。不过只要大家都遵循 SRP,那么团队中的每一个人都能够开心工作,业务代码易读,健壮性强,可复用性强,你说开不开心。
看一个购物车的实例
class Cart {
// 添加商品
public func addProduct() { }
// 移除商品
public func deleteProduct() {}
// 获取商品
public func getProduct() {}
// 设定订单信息
public func setOrderInfo(_ info: InfoModel){}
// 取得付款信息
public func getPaymentInfo(){}
// 保存订单
public func saveOrder(){}
}
可以看到上面Cart类中不仅有商品的操作也有订单相关的操作,其实是不符合单一职责原则的。试想,假如其他地方想使用订单,那么需要创建购物车的实例才可以使用,增加了一些不必要的操作,如果我们把订单作为一个单独的类来处理,那么购物车和订单之间相互没有关系,使用起来非常方便。
class Cart {
// 添加商品
public func addProduct() { }
// 移除商品
public func deleteProduct() {}
// 获取商品
public func getProduct() {}
}
class Order {
// 设定订单信息
public func setOrderInfo(_ info: InfoModel){}
// 取得付款信息
public func getPaymentInfo(){}
// 保存订单
public func saveOrder(){}
}
可以看到现在使用起来就非常容易了,不管哪里需要订单内容,实例化Order对象进行相关操作即可,而且对订单进行修改也不会影响购物车,这完全体现了SRP。总之,自己做自己的事情,不要做跟自己不相关的事情就好。
参考