1、定义
首先呢,我们来看一下单一职责原则的定义。
就一个类而言,应该只有一个引起它变化的原因
这个说法不是很好懂,有一些抽象,不过呢,我们依旧可以尝试着理解一下。
就一个类而言,只有一个引起它变化的原因,也就是说,除此之外,不能有其它引起变化的原因。
这样就需要一个前提,这个类只能负责一项职责,而不能负责其他的职责,不然,其他的职责就会存在其他变化的原因了。
通俗的说,即一个类只负责一项职责。
懒人就比较喜欢这种通俗地定义,一目了然。
懒人曾经总结过:通俗的定义,浅显易懂;理论的定义,大脑一懵。
有同感的小伙伴请双击666。
2、场景
餐馆聚餐,通过服务员点餐
这是一个比较常见的场景,比如懒人撸了五天的代码,身心疲惫,周末的时候呢,就约上三五个好友,去餐馆(番茄餐厅)happy一下(非常单纯的吃饭)。我们刚刚坐下,就来了一位很漂亮的服务员为我们点餐。
这样一个服务员为我们点餐的场景,一般都是什么样的流程?
第一步:客人点餐
懒人:咱呢,不但要吃饱,还要吃好!服务员,先来一份西红柿炒鸡蛋,再来一份酸辣土豆丝!!!
好友:脸呢。。。说好的脸呢。。。
服务员:你是顾客,你是上帝,你说啥就是啥,不过,你刚才说的是啥。。。
第二步:烹饪美食
西红柿炒鸡蛋,先炒鸡蛋,再炒西红柿。。。ok,出锅。
第三步:上餐
服务员:这是您点的西红柿炒鸡蛋,请您慢用。
3、实现
不废话,撸代码。
package com.fanqiekt.principle.single;
/**
* 服务员
* @Author: 番茄课堂-懒人
*/
public class Waiter {
/**
* 下单
* @param dishName 菜名
*/
public void order(String dishName){
System.out.println("客人点餐:" + dishName);
System.out.println("开始烹饪:" + dishName);
//菜品不同,做法不同。
switch (dishName){
case "西红柿炒鸡蛋":
System.out.println("先炒鸡蛋");
System.out.println("再炒西红柿");
System.out.println("...");
break;
case "酸辣土豆丝":
System.out.println("先放葱姜蒜");
System.out.println("再放土豆丝");
System.out.println("...");
break;
}
System.out.println(dishName + "出锅");
System.out.println(dishName + "上桌啦,请您品尝");
}
}
复制代码
服务员这个类比较简单,就一个下单的方法。
为了更好的理解,懒人进行了细节的优化(主要是很多细节懒人压根不了解)。
package com.fanqiekt.principle.single;
/**
* 客户端
* @Author: 番茄课堂-懒人
*/
public class Client {
public static void main(String[] args){
Waiter waiter = new Waiter();
waiter.order("西红柿炒鸡蛋");
System.out.println("-------");
waiter.order("酸辣土豆丝");
}
}
复制代码
客户端这个类就相当于客人,客人负责通过服务员点餐。
客人一共点了两道大餐,西红柿炒鸡蛋、酸辣土豆丝,我们来运行一下,看看结果。
客人点餐:西红柿炒鸡蛋
开始烹饪:西红柿炒鸡蛋
先炒鸡蛋
再炒西红柿
...
西红柿炒鸡蛋出锅
西红柿炒鸡蛋上桌啦,请您品尝
-------
客人点餐:酸辣土豆丝
开始烹饪:酸辣土豆丝
先放葱姜蒜
再放土豆丝
...
酸辣土豆丝出锅
酸辣土豆丝上桌啦,请您品尝
复制代码
OK,两个热气腾腾的饭菜就做好了。
我们回过头来看一下waiter类,大家觉得这个类好不好?
肯定是不好了,那…不好在哪里?
这就好比一个小作坊,老板既负责点餐又负责下单,就跟waiter类一样。
我们一般在小作坊吃饭,感受会怎么样?
乱,非同一般的杂乱。上菜需要等半天,点餐的时候找不到人。
还有一个弊端,我修改了做饭的流程,会影响下单的业务,增加修改的风险,为什么这么说呢?
客人A:老板,给我来一份酸辣土豆丝。
老板:好嘞,您稍等。
懒人:老板,我刚才点的西红柿鸡蛋要少放盐啊。
老板:好的,我放盐的时候用小点的勺子。
客人A:老板,我的菜做了吗?我的同伴都吃完了,没做我就不要了!
老板:您的菜已经做了,马上就要出锅了。(内心:我勒个去,刚才用小勺放盐的时候把这哥们点的单给忘了,这就尴尬了。。。)
不难看出,当功能冗杂到一个对象中,这样修改就会增加风险。那我们该如何避免呢?
一般比较完善的餐馆,还至少会有一名厨师。
厨师做饭,服务员点餐,这样做,有什么好处呢?
一来,结构清晰了,各司其职,一目了然。二来,风险降低了,我修改做饭的流程,不会影响下单的业务。
只负责一项职责,这就是单一职责原则。
那我们尝试着增加一个厨师类。
package com.fanqiekt.principle.single;
/**
* 厨师
*
* @author 番茄课堂-懒人
*/
public class Chef {
/**
* 做饭
* @param dishName 下单的菜名
*/
public void cooking(String dishName) {
System.out.println("开始烹饪:"+dishName);
switch (dishName){
case "西红柿炒鸡蛋":
System.out.println("先炒鸡蛋");
System.out.println("再炒西红柿");
System.out.println("...");
break;
case "酸辣土豆丝":
System.out.println("先放葱姜蒜");
System.out.println("再放土豆丝");
System.out.println("...");
break;
}
System.out.println(dishName + "出锅");
}
}
复制代码
厨师类,只负责了一项职责:做饭。
这就是类的单一职责原则。
Chef类只有一个cooking方法,cooking方法是根据下单的菜品名称去烹饪不同的菜,以及炒西红柿鸡蛋以及酸辣土豆丝的具体烹饪过程。这样做合适吗?
不合适的,cooking方法应该只有菜品分发这一项职责,而炒西红柿鸡蛋以及酸辣土豆丝这两件事显然易见与分发没有任何关系,所以拆分出来效果会更好。
我们将厨师类再优化下。
package com.fanqiekt.principle.single;
/**
* 厨师
*
* @author 番茄课堂-懒人
*/
public class Chef {
/**
* 做饭
* 方法的单一职责原则
* @param dishName 下单的菜名
*/
public void cooking(String dishName) {
System.out.println("开始烹饪:"+dishName);
switch (dishName){
case "西红柿炒鸡蛋":
cookingTomato();
break;
case "酸辣土豆丝":
cookingPotato();
break;
}
System.out.println(dishName + "出锅");
}
/**
* 炒西红柿鸡蛋
*/
private void cookingTomato() {
System.out.println("先炒鸡蛋");
System.out.println("再炒西红柿");
System.out.println("...");
}
/**
* 炒酸辣土豆丝
*/
private void cookingPotato() {
System.out.println("先放葱姜蒜");
System.out.println("再放土豆丝");
System.out.println("...");
}
}
复制代码
优化后Chef类有三个方法。
cooking方法是根据下单的菜品名称去烹饪不同的菜。
cookingTomato方法是炒西红柿鸡蛋。
cookingPotato方法是炒酸辣土豆丝。
每个方法只负责一项职责,这就是方法的单一职责原则。
遵守方法单一职责原则的类,是不是更加的直观?修改各自的方法是不是也没有影响到其他的方法?
接下来,我们再优化下Waiter类,让他遵循类的单一职责原则。
package com.fanqiekt.principle.single;
/**
* 单一职责原则的服务员
*
* @author 番茄课堂-懒人
*/
public class Waiter {
private Chef chef = new Chef();
/**
* 点餐
* @param dishName 餐名
*/
public void order(String dishName) {
System.out.println("客人点餐:"+dishName);
chef.cooking(dishName);
System.out.println(dishName+"上桌啦,请您品尝!");
}
}
复制代码
优化后SingleWaiter类有只负责点餐、上餐这些与服务员相关的职责,而做饭的这些无关的职责则交给了Chef。
遵守类单一职责原则的项目,是不是更加的直观?修改各自的类是不是也没有影响到其他的类?
接下来,我们把Client运行一下。
客人点餐:西红柿炒鸡蛋
开始烹饪:西红柿炒鸡蛋
先炒鸡蛋
再炒西红柿
...
西红柿炒鸡蛋出锅
西红柿炒鸡蛋上桌啦,请您品尝
-------
客人点餐:酸辣土豆丝
开始烹饪:酸辣土豆丝
先放葱姜蒜
再放土豆丝
...
酸辣土豆丝出锅
酸辣土豆丝上桌啦,请您品尝
复制代码
结果与原来一致。
4、优点
撸过代码后,我们发现单一职责原则的几个优点。
提高类的可读性
符合单一职责原则的方法、类,结构会更加的清晰,类的可读性也就提高了。
降低类的复杂性 一个类只负责一项职责,一个方法也只负责一项职责。肯定要比功能冗杂到一个方法,一个类中要简单得多。
降低风险
修改其中的一个业务,不会影响到业务。
5、总结
我们必须要意识到,一味的遵守单一职责原则,不停的分拆类所付出的开销是很大的。
这时候就涉及到平衡的问题,平衡单一职责原则与修改造成的开销。
懒人的观点是如果一个方法逻辑不复杂的情况下,可以修改方法实现,否则要拆分为两个方法,遵循方法级别的单一职责原则。
如果一个类方法不多的情况下,可以只增加方法,而不用分拆为多个类,否则要拆分为多个类,遵循类级别的单一职责原则。
6、嘻哈说
接下来,请您欣赏单一职责原则的原创歌曲。
嘻哈说:单一职责原则
作曲:懒人
作词:懒人
Rapper:懒人
周末约上了好友去熟悉的餐馆聚餐
只负责点餐的漂亮服务员保持笑容已经成为习惯
只负责做饭的帅气厨师一直待在了烟雾弥漫了几遍的厨房里面
每个人有自己负责的地盘
就像单一职责
一个类只有一个职责 好体面
它降低了类的复杂性
它提高了类的可读性
那风险被降低代表着单一职责没毛病
复制代码
闲来无事听听曲,知识已填脑中去;
学习复习新方式,头戴耳机不小觑。
番茄课堂,学习也要酷。