本文分享自华为云社区《如何消除代码屎山中的一大坨参数列表?》,作者: JavaEdge 。

有经验的程序员应该都见过,一个方法坐拥几十上百个参数。

1 方法为何要有参数?

因为不同方法之间需要共享信息。

但方法间共享信息的方式除了参数列表,还有全局变量。但全局变量总能带来意外之喜,所以,取消全局变量也是各大语言趋势。于是参数列表就成了唯一选择,于是,只要你想到有什么信息要传给一个方法,就会直接将其加入参数列表,导致参数列表越来越长!

2 长参数列表怎么了?

参数列表一旦过长,你一个 crud boy就很难完全掌控这些逻辑了呀!所以症结是数量多,解决关键也就是降低参数数量。

3 解决方案

3.1 聚沙成塔

一个简单的创建博客的方法:

public void createActicle(final String title, final String introduction,final URL coverUrl,final ActicleType type,final ActicleColumn column,final String protagonists,final String tags,final boolean completed) {...Acticle acticle = Acticle.builder.title(title) .introduction(introduction).coverUrl(coverUrl).type(type).column(column).protagonists(protagonists).tags(tags).completed(completed).build();this.repository.save(acticle);
}

参数列表包含了一篇博客所要拥有的各种信息,比如:博客标题、简介、封面 URL、类型、归属专栏、主角姓名、标签、是否完结…

若只是想理解逻辑,可能你还会觉得这参数列表挺好啊,把创建一篇博客所需的信息都传给了方法,这也是大部分人面对一段代码时理解问题的最初角度。虽然这样写代码容易让人理解,但不足以让你发现问题。

现在产品要求在博客里增加一项信息,标识这部博客是否收费,咋办?很简单啊!我直接新增一个参数。很多屎山就这么来的,积少成多,量变引起质变!

这里所有参数都是创建博客所必需,所以,可以做的就是将这些参数封装成一个类,一个创建博客的参数类:

public class NewActicleParamters {private String title;private String introduction;private URL coverUrl;private ActicleType type;private ActicleColumn column;private String protagonists;private String tags;private boolean completed;...
}

这样参数列表就只剩下一个参数了:

public void createActicle(final NewActicleParamters parameters) {...
}

所以, 将参数列表封装成对象吧 !

只是把一个参数列表封装成一个类,然后,用到这些参数的时候,还需要把它们一个个取出来,这会不会是多此一举呢?就像这样:

public void createActicle(final NewActicleParamters parameters) {...Acticle acticle = Acticle.builder.title(parameters.getTitle()) .introduction(parameters.getIntroduction()).coverUrl(parameters.getCoverUrl()).type(parameters.getType()).channel(parameters.getChannel()).protagonists(parameters.getProtagonists()).tags(parameters.getTags()).completed(parameters.isCompleted()).build();this.repository.save(acticle);
}

若你这样想,说明还没有形成对软件设计的理解。我们并非只是简单地把参数封装成类,站在设计角度,这里引入的是一个新模型。
一个模型的封装应该以【行为】为基础
之前没有这个模型,所以想不到它应该有什么行为,现在模型产生了,它就该有自己配套的行为。

该模型的行为就是构建一个博客对象,则代码就能进一步重构:

public class NewActicleParamters {private String title;private String introduction;private URL coverUrl;private ActicleType type;private ActicleColumn column;private String protagonists;private String tags;private boolean completed;public Acticle newActicle() {return Acticle.builder.title(title) .introduction(introduction).coverUrl(coverUrl).type(type).column(column).protagonists(protagonists).tags(tags).completed(completed).build();}
}

创建博客的方法就得到极大简化:

public void createActicle(final NewActicleParamters parameters) {...Acticle acticle = parameters.newActicle();this.repository.save(acticle);
}

如此,一旦后续需求又扩展了,需要增加创建博客所需的内容,则该参数列表是不变的,也就是说它是稳定的!

3.2 动静分离

若这个类不断膨胀,变成一个大类,该咋办?毕竟并非所有场景下,参数都属于一个类:

// 根据博客 ID 获取对应章节信息
public void getSections(final long acticleId, final HttpClient httpClient,final SectionProcessor processor) {HttpUriRequest request = createChapterRequest(acticleId);HttpResponse response = httpClient.execute(request);List<Section> sections = toSections(response);processor.process(sections);
}

单论参数的个数,数量并不多。若你只看这个方法,很难发现直接问题。绝对数量不是core,参数列表也应该是越少越好。

但注意,每次传进来的参数:

  • acticleId 都随请求不同而改变
  • 但 httpClient、processor 两个参数一样,因为都有相同逻辑,没啥变化。
    即acticleId 的变化频率同 httpClient 和 processor 这两个参数变化频率不同。

**不同的数据变动方向也是不同关注点。**这里表现出来的就是典型的动数据(acticleId)和静数据(httpClient、processor),它们是不同关注点,应该分离。

针对该案例:

  • 静态不变的数据完全可以成为这个方法所在类的一个字段
  • 只将每次变动的东西作为参数传递即可

因此,代码可重构如下:

public void getSections(final long acticleId) {HttpUriRequest request = createChapterRequest(acticleId);HttpResponse response = this.httpClient.execute(request);List<Section> sections = toSections(response);this.processor.process(sections);
}

这个坏味道是个软件设计问题,代码缺乏应有的结构,所以,原本应该属于静态结构的部分却以动态参数的方式传来传去,无形中导致参数列表变长。

长参数列表固然可以用一个类进行封装,但能够封装成这个类的前提是:这些参数属于一个类,有相同的变化原因!

若方法的参数有不同变化频率,就要看情况了。对静态部分,本小节案例已经看出,它可以成为软件结构的一部分,而若有多个变化频率,则还可以封装出多个参数类。

3.3 再见了,标记!

public void editChapter(final long chapterId, final String title, final String content, final boolean apporved) {...
}

待修改章节的ID、标题和内容,最后一个参数表示这次修改是否直接审核通过。

前面几个参数是修改一个章节的必要信息,重点在最后这个参数。
从业务上说,如果是作者进行编辑,之后要经过审核,而如果编辑来编辑的,那审核就直接通过,因为编辑本身扮演了审核人的角色。所以,你发现了,这个参数实际上是一个标记,标志着接下来的处理流程会有不同。

使用标记参数,是程序员初学编程时常用的一种手法。正是这种手法实在太好用,导致代码里flag肆意飘荡。不仅变量里有标记,参数里也有。很多长参数列表其中就包含了各种标记参数。

在实际的代码中,必须小心翼翼地判断各个标记当前的值,才能做好处理。

解决标记参数,一种简单的方式就是,将标记参数代表的不同路径拆分出来。

这里的一个方法可以拆分成两个方法,一个方法负责“普通的编辑”,另一个负责“可以直接审核通过的编辑”。

// 普通的编辑,需要审核
public void editChapter(final long chapterId, final String title, final String content) {...
}
// 直接审核通过的编辑
public void editChapterWithApproval(final long chapterId,final String title,final String content) {...
}

标记参数在代码中存在的形式很多,有的是布尔值、枚举值、字符串或整数。都可以通过拆分方法的方式将它们拆开。在重构中,这种手法叫做移除标记参数(Remove Flag Argument)。

只有短小的代码,我们才能有更好地把握,而要写出短小的代码,需要我们能够“分离关注点”。

总结

应对长参数列表主要的方式就是减少参数的数量,最直接的就是将参数列表封装成一个类。但并不是说所有的情况都能封装成类来解决,我们还要分析是否所有的参数都有相同的变动频率。

变化频率相同,则封装成一个类。
变化频率不同的话:

  • 静态不变的,可以成为软件结构的一篇分
  • 多个变化频率的,可以封装成几个类

此外,参数列表中经常会出现标记参数,这是参数列表变长的另一个重要原因。对于这种标记参数,一种解决方案就是根据这些标记参数,将方法拆分成多个方法。

点击关注,第一时间了解华为云新鲜技术~​

如何消除代码山中那一大坨参数列表相关推荐

  1. 如何消除代码屎山中的一大坨参数列表?

    有经验的程序员应该都见过,一个方法坐拥几十上百个参数. 方法为何要有参数? 因为不同方法间需共享信息. 但方法间共享信息的方式不止一种,除了参数列表,还有全局变量.但全局变量总能带来意外惊喜,所以,取 ...

  2. R语言可视化绘制及PDF使用字体参数列表:查看字体列表、可视化绘制图像中的字体参数列表、字体示例并写入pdf

    R语言可视化绘制及PDF使用字体参数列表:查看字体列表.可视化绘制图像中的字体参数列表.字体示例并写入pdf 目录 R语言可视化绘制及PDF使用字体参数列表:查看字体列表.可视化绘制图像中的字体参数列 ...

  3. C语言中函数的参数列表为空和void的区别

    C标准和C++标准在main函数上是有区别的,所以我分别详细来介绍他们. 对于C语言: C89标准的main()函数是可接受的,尽管现在建议是使用C99的标准.C99标准只定义了如下两种可接受的函数原 ...

  4. c语言全局变量作为参数_在C / C ++中使用变量参数列表

    c语言全局变量作为参数 C/C++ provides a means to pass a variable number of arguments to a function.  This artic ...

  5. c语言理解参数,c语言中对可变参数列表的简单理解

    函数原型中一般情况下参数的数目是固定的,但是如果想在不同的时候接收不定数目的参数时该怎么办呢?c语言提供了可变参数列表来实现. 可变参数列表是通过宏来实现的,这些宏定义在stdarg.h的头文件中.头 ...

  6. linux中 tar 报参数列表过长,四种解决”Argument list too long”参数列表过长的办法...

    在linux中删除大量文件时,直接用rm会出现:-bash: /bin/rm: 参数列表过长,的错误. 这时可以用find命令来结合使用. 例: 1.rm * -rf 改为: find . -name ...

  7. 【Android Gradle 插件】gradle.properties 中配置编译参数并在 Java 代码 BuildConfig 中调用该参数

    文章目录 一.gradle.properties 中配置编译参数 二.在 build.gradle 中配置 BuildConfig.java 生成信息 三.编译后生成的 BuildConfig 类 A ...

  8. 【Groovy】闭包 Closure ( 闭包参数列表规则 | 默认参数列表 | 不接收参数 | 接收自定义参数 )

    文章目录 一.闭包参数列表 二.闭包参数列表代码示例 一.闭包参数列表 闭包的参数设置有如下情况 : 不接收参数 : 如果在定义闭包时 , 只写了 " -> " 符号 , 没 ...

  9. 如何正确的使用Java8中的Optional类来消除代码中的null检查

    点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试文章 作者:一书生VOID lw900925.github.io/jav ...

最新文章

  1. 多个网站共享一个mysql数据库_如何在多个Postgresql数据库之间共享表
  2. C# + MySql 存贮过程开发示例
  3. 20165301 预备作业三:Linux安装及命令入门
  4. 【设计模式】适配器模式 ( 概念 | 适用场景 | 优缺点 | 外观模式对比 | 适配器模式相关角色 | 类适配器 | 对象适配器 | 实现流程 )
  5. USTC English Club Note20171013(2)
  6. 网络——Cisco Packet Tracer 思科模拟器组网实验
  7. Linux下Web效力器架设攻略-1
  8. 人事面试的那些问题及背后的考察点
  9. MyEclipse 10.5 安装SVN插件
  10. larvel php restful_laravel 实现一个简单的 RESTful API
  11. 超分辨率技术如何发展?这6篇ECCV 18论文带你一次尽览
  12. 51NOD 1449 砝码称重(贪心+进制思想)
  13. PHP如何获取当前域名
  14. There appears to be trouble with your network connection
  15. Cylons工业机器人_机器人的是什么意思
  16. The Annotated Transformer(解读Transformer)
  17. otf是什么格式?怎么安装呢?
  18. sasl java_java SASL_SSL 帐号密码 方式访问 kafka
  19. Zxing图片识别 从相册选二维码图片解析总结
  20. [Luogu3600] 随机数生成器 [概率期望动态规划 拉格朗日插值离散微积分]

热门文章

  1. 前端:HTML/03/超级链接,绝对地址URL,相对地址URL,特殊链接
  2. java面向对象程序课本,Java面向对象程序设计
  3. A*算法一个简单的记录
  4. 使用仿真软件查看机器人在一条直线上移动的 configuration
  5. 视觉SLAM笔记(54) Ceres 操作后端优化
  6. linux线程能删除自身吗,Linux内核本身和进程的区别 内核线程、用户进程、用户...
  7. java 正则提取大于等于号_Java正则表达式
  8. 如何修改php的网页文件,php如何修改php文件内容
  9. js与jquery操作
  10. python第九十一天----第十六周作业