重构 - 保持函数的单一职责

前端之家收集整理的这篇文章主要介绍了重构 - 保持函数的单一职责前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

1.前言

上篇文章写了添加扩展性方面的重构,讲到了开放封闭原则。学习的步伐不能就此停止,今天的文章,将会提及开发的另一个原则:单一职责原则。通俗点说就是一个函数只做一件事,下面将会通过几个实例,探究单一职责原则。

2.单一职责表现形式

单一职责的定义可以理解为:一个对象或者方法,只做一件事。

遵守单一职责的实例太多了,下面简单列举一下。

原生的API方面

trimRight()和trimLeft():trimRight 只负责去除右边的空白,其它地方一概不管。 trimLeft 只负责去除右边的空白,其它地方也一概不关。

concat(): concat 只负责连接两个或更多的数组,并返回结果。不会涉及删除数组的操作。

toFixed(): toFixed 只把 Number 类型的值四舍五入为指定小数位数的数字。不会执行其它操作。

JQuery 的 API

$.each() 只负责遍历,要处理什么,自己再动手操作。

css() 只负责设置 DOM 的 style ,不会设置 innerHTML 。

animate() 只负责执行 CSS 属性集的自定义动画,不会涉及其它操作。

说是这样说,但是大家看着可能会有点懵,看不出来遵守单一原则有什么好处,下面看一个实例。

3.实例-数组处理

如下例子:

现有一批的录入学生信息,但是数据有重复,需要把数据根据 id 进行去重。然后把为空的信息,改成'--'。

  1. let students=[
  2. {
  3. id:5,name:'守候',sex:'男',age:'',},{
  4. id:2,name:'浪迹天涯',age:''
  5. },{
  6. id:5,sex:'',{
  7. id:3,name:'鸿雁',age:'20'
  8. }
  9. ];
  10.  
  11. function handle(arr) {
  12. //数组去重
  13. let _arr=[],_arrIds=[];
  14. for(let i=0;i<arr.length;i++){
  15. if(_arrIds.indexOf(arr[i].id)===-1){
  16. _arrIds.push(arr[i].id);
  17. _arr.push(arr[i]);
  18. }
  19. }
  20. //遍历替换
  21. _arr.map(item=>{
  22. for(let key in item){
  23. if(item[key]===''){
  24. item[key]='--';
  25. }
  26. }
  27. });
  28. return _arr;
  29. }
  30. console.log(handle(students))

运行结果没有问题,但是大家想一下,

1.如果改了需求,比如,学生信息不会再有重复的记录,要求把去重的函数去掉,无论,就是整个函数都要改了,还影响到下面的操作。

2.如果项目另一个地方也是同样的操作,但是不需要去重。这样只能再写一个基本一样的函数,因为上面的函数无法复用。如下

  1. function handle1(arr) {
  2. //数组深拷贝
  3. let _arr=JSON.parse(JSON.stringify(arr));
  4. //遍历替换
  5. _arr.map(item=>{
  6. for(let key in item){
  7. if(item[key]===''){
  8. item[key]='--';
  9. }
  10. }
  11. });
  12. return _arr;
  13. }

3.如果项目有一个地方还需要根据 ID 排序。这样还是得写一个函数,因为在不能在上面的函数上面排序。

  1. function handle2(arr) {
  2. //数组去重
  3. let _arr=[],_arrIds=[];
  4. for(let i=0;i<arr.length;i++){
  5. if(_arrIds.indexOf(arr[i].id)===-1){
  6. _arrIds.push(arr[i].id);
  7. _arr.push(arr[i]);
  8. }
  9. }
  10. //遍历替换
  11. _arr.map(item=>{
  12. for(let key in item){
  13. if(item[key]===''){
  14. item[key]='--';
  15. }
  16. }
  17. });
  18. //根据ID排序
  19. _arr.sort((item1,item2)=>item1.id-item2.id);
  20. return _arr;
  21. }

这样的问题就是在于,面对需求的变化,不能灵活的处理。函数也基本没办法复用。

下面使用单一原则构造一下

  1. let handle={
  2. //数组去重
  3. removeRepeat(arr){
  4. let _arr=[],_arrIds=[];
  5. for(let i=0;i<arr.length;i++){
  6. if(_arrIds.indexOf(arr[i].id)===-1){
  7. _arrIds.push(arr[i].id);
  8. _arr.push(arr[i]);
  9. }
  10. }
  11. return _arr;
  12. },//遍历替换
  13. setInfo(arr){
  14. arr.map(item=>{
  15. for(let key in item){
  16. if(item[key]===''){
  17. item[key]='--';
  18. }
  19. }
  20. });
  21. return arr;
  22. },//根据id排序
  23. sortForId(arr){
  24. return arr.sort((item1,item2)=>item1.id-item2.id);
  25. }
  26. };
  27. //去重
  28. students=handle.removeRepeat(students);
  29. //设置信息
  30. students=handle.setInfo(students);
  31. console.log(students);

结果一样,而且这样的方式,可以使得方法可以组合使用,更加的灵活,也方便复用。

如果还需要根据ID排序,就在上面代码执行结果的基础上,再加一行代码即可。

  1. //根据ID排序
  2. students=handle.sortForId(students);
  3. console.log(students);

如果原始数据不需要去重,设置完信息之后,直接排序

  1. let students=[
  2. {
  3. id:5,age:'20'
  4. }
  5. ];
  6. //设置信息
  7. students=handle.setInfo(students);
  8. //根据ID排序
  9. students=handle.sortForId(students);

这样操作起来,即使以后需求有改动,在可控的范围内,可以灵活的组合使用,函数也可以复用。

如果觉得要让 students 连续赋值麻烦,可以借鉴 JQuery 的链式调用方式。

  1. let ec=(function () {
  2. let handle=function (obj) {
  3. this.obj=JSON.parse(JSON.stringify(obj));
  4. };
  5. handle.prototype={
  6. /**
  7. * @description 去重
  8. */
  9. unique(){
  10. //根据id数组去重
  11. let _arr=[],_arrIds=[];
  12. for(let i=0;i<this.obj.length;i++){
  13. if(_arrIds.indexOf(this.obj[i].id)===-1){
  14. _arrIds.push(this.obj[i].id);
  15. _arr.push(this.obj[i]);
  16. }
  17. }
  18. this.obj=_arr;
  19. return this;
  20. },/**
  21. * @description 设置保密信息
  22. */
  23. setInfo(){
  24. this.obj.map(item=>{
  25. for(let key in item){
  26. if(item[key]===''){
  27. item[key]='--';
  28. }
  29. }
  30. });
  31. return this;
  32. },sortForId(){
  33. this.obj.sort((item1,item2)=>item1.id-item2.id);
  34. return this;
  35. },/**
  36. * @description 返回处理结果
  37. * @return {Array|*}
  38. */
  39. end(){
  40. return this.obj;
  41. }
  42. }
  43. //暴露构造函数接口
  44. return function (obj) {
  45. return new handle(obj);
  46. }
  47. })();
  48. let students=[
  49. {
  50. id:5,age:'20'
  51. }
  52. ];
  53. //根据id去重和设置'--'
  54. students=ec(students).unique().setInfo().end();
  55. console.log(students)

结果还是一样,只是增加了一个方法,方便链式调用

关于实现链式调用,这个肯定是会增加代码的,如果调用方法并不是一些常用,通用的方法的话,只是处理一些特殊格式的数据的方法(如上实例),不建议花费时间,实现链式调用,普通调用就好。如果是一些常用的函数的封装,就建议使用链式调用

4.违反单一职责原则

在上面的实例里面,相信大家都看到了,遵守单一职责的好处,但是单一职责也有缺点,就是会增加代码的复杂程度。

在市面上,也有API是违反单一职责的。

JQuery 的 html() 方法,既可以获取 innerHTML ,也可以设置 innerHTML 。 attr ()既可以获取 DOM 元素的某一个属性,也可以设置 DOM 元素的某一个属性

在维护上面,这样的代码,可能会给维护增加难度,但是对于使用者而言,这样简化了使用。这应该是一个取舍关系,取什么,舍什么。这个就是具体情况具体分析。

5.小结

今天的例子就到这里了,这个例子,解释降解函数单一原则会有什么好处。这个例子比上篇文章的例子还要简单。大家看不明白,把代码拷贝在浏览器上运行,就很好理解。如果大家对这个例子有什么更好的建议,或者代码上有什么问题,欢迎在评论区留言,大家多交流,相互学习。

---------------------华丽的分割线---------------------

想了解更多,关注关注我的微信公众号:守候书阁

猜你在找的设计模式相关文章