Java杂谈之如何消除代码中一大串参数列表

目录
  • 方法为何要有参数?
  • 长参数列表的问题
  • 解决方案
    • 聚沙成塔
    • 动静分离
    • 告别标记
  • 总结

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

方法为何要有参数?

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

但方法间共享信息的方式不止一种,除了参数列表,还有全局变量。但全局变量总能带来意外惊喜,所以,取消全局变量也是各大语言的趋势。

但方法之间还是要传递信息的,不能用全局变量,于是参数就成了唯一选择,于是,只要你想到有什么信息要传给一个方法,就会直接它加到参数列表中,参数列表也越来越长。

长参数列表的问题

参数列表过长,你一个 crud 程序员就很难完全掌控这些逻辑了呀!

所以症结是数量多,解决关键也就在于降低参数的数量。

解决方案

聚沙成塔

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

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);
}

“如何扩展需求”?如果需求扩展,需要增加创建博客所需的内容,那这个参数列表就是不变的,相对来说,它就是稳定的。

那这个类就会不断膨胀,变成一个大类,那该怎么办呢?如何解决大类?

动静分离

不是所有情况下,参数都属于一个类:

public void getChapters(final long acticleId,
                        final HttpClient httpClient,
                        final ChapterProcessor processor) {
  HttpUriRequest request = createChapterRequest(acticleId);
  HttpResponse response = httpClient.execute(request);
  List<Chapter> chapters = toChapters(response);
  processor.process(chapters);
}

根据博客 ID 获取其对应章节信息。
纯以参数个数论,参数数量不多。

如果你只是看这个方法,可能很难发现直接问题。绝对数量不是关键点,参数列表也应该是越少越好。

在这几个参数里面,每次传进来的 acticleId 都不一样,随请求不同而改变。但 httpClient 和 processor 两个参数一样,因为都有相同逻辑,没什么变化。
即acticleId 的变化频率同 httpClient 和 processor 这两个参数变化频率不同。

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

具体到这个场景下,静态不变的数据完全可以成为这个方法所在类的一个字段,而只将每次变动的东西作为参数传递就可以了。按照这个思路,代码可以改成这个样子:

public void getChapters(final long acticleId) {
  HttpUriRequest request = createChapterRequest(acticleId);
  HttpResponse response = this.httpClient.execute(request);
  List<Chapter> chapters = toChapters(response);
  this.processor.process(chapters);
}

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

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

如果方法的参数有不同的变化频率,就要视情况而定了。对于静态的部分,我们前面已经看到了,它可以成为软件结构的一篇分,而如果有多个变化频率,我们还可以封装出多个参数类。

告别标记

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)。

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

总结

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

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

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

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

减少参数列表,越少越好。

到此这篇关于Java杂谈之如何消除代码中一大串参数列表的文章就介绍到这了,更多相关Java 参数列表内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Java程序命令行参数用法总结

    目录 基于命令行输入参数 基于命令行输入系统属性 JVM参数设置 环境变量 vs 系统属性 基于Maven命令运行Spring Boot应用 前言: 在命令行中输入可以输入各类参数,本文将针对这些参数做一个小结. 基于命令行输入参数 测试程序如下: import java.util.Arrays; public class Main { public static void main(String[] args) { System.out.println("System Property, ab

  • java接口返回参数按照请求参数进行排序方式

    目录 java接口返回参数按照请求参数进行排序 排序 java通过接口进行排序 描述 知识点 1.Comparable接口 2.Comparator接口 java接口返回参数按照请求参数进行排序 在项目实际开发中可能遇到过这种问题,接口请求参数顺序是[a,b,c],结果返回的数据是[bObject,cObject,aObject],导致这种原因可能是底层采用了设计模式,或者是表拼接查询,本文主要就是为了实现这种功能,采用流的方法 代码实现 import lombok.Data; import j

  • 解析Java的可变长参数列表及其使用时的注意点

    Java 可变参数列表 复制代码 代码如下: class A {} 由于所有的类都继承于Object,可以以Object数组为参数的方法: public class parameter { static void printArray(Object[] args){ for(Object obj : args){ System.out.print(obj + " "); } System.out.println(); } public static void main(String[]

  • Java可变参数列表详解

    Java可变参数列表详解 1.接受的传入参数情况: 如public void test(String ...args){...} 1)不使用参数,如test() 2)使用一个或多个参数,如test("1"); test("1","2"); 3)使用数组 test(new String[]{"1","2"}); 2.方法内部访问参数: 在test方法内部,我们可以像使用数组的访问方式一样来访问参数args.如

  • 一篇文章教你如何用Java自定义一个参数校验器

    目录 注解 校验器 异常处理 测试 总结 自定义一个唯一字段校验器 注解 @Target({ElementType.FIELD}) @Retention(RetentionPolicy.RUNTIME) @Documented @Constraint(validatedBy = {IsUniqueValidator.class}) // 指定自定义的校验器 public @interface IsUnique { // 提示信息 String message() default "";

  • Java杂谈之如何消除代码中一大串参数列表

    目录 方法为何要有参数? 长参数列表的问题 解决方案 聚沙成塔 动静分离 告别标记 总结 有经验的程序员应该都见过,一个方法坐拥几十上百个参数. 方法为何要有参数? 因为不同方法间需共享信息. 但方法间共享信息的方式不止一种,除了参数列表,还有全局变量.但全局变量总能带来意外惊喜,所以,取消全局变量也是各大语言的趋势. 但方法之间还是要传递信息的,不能用全局变量,于是参数就成了唯一选择,于是,只要你想到有什么信息要传给一个方法,就会直接它加到参数列表中,参数列表也越来越长. 长参数列表的问题 参

  • 在java代码中获取JVM参数的方法

    实例如下: MemoryMXBean memorymbean = ManagementFactory.getMemoryMXBean(); MemoryUsage usage = memorymbean.getHeapMemoryUsage(); System.out.println("INIT HEAP: " + usage.getInit()); System.out.println("MAX HEAP: " + usage.getMax()); System.

  • Spring如何消除代码中的if-else/switch-case

    前言 在很多时候,我们代码中会有很多分支,而且分支下面的代码又有一些复杂的逻辑,相信很多人都喜欢用 if-else/switch-case 去实现.做的不好的会直接把实现的代码放在 if-else/switch-case 的分支之下: switch ( type ) { case case1: ... ... break; case case2: ... ... break; case case3: ... ... break default: return null; } 这样的代码不仅冗长,

  • 轻松学习JavaScript函数中的 Rest 参数

    JavaScript函数可以使用任意数量的参数.与其他语言(如C#和Java)不同,你可以在调用JavaScript函数时传递任意数量的参数.JavaScript函数允许未知数量的函数参数.在ECMAScript 6之前,JavaScript有一个变量来访问这些未知或可变数目的参数,这是一个类似数组的对象,并非一个数组.细想以下代码来理解arguments变量: function add(){ var result = 0; for(let i=0;i<arguments.length;i++)

  • Java杂谈之类和对象 封装 构造方法以及代码块详解

    目录 1. 类和对象的初步认知 2. 类的实例化 3. 类的成员 字段(属性/成员变量) 方法 static 关键字 修饰字段 修饰方法 修饰代码块(暂不讲) 修饰类(暂不讲) 4. 封装 5. 构造方法 6. this 用法 关于引用的几个注意事项: 7. 代码块 Java当中的类和对象 1. 类和对象的初步认知 java 是一门面向对象的语言,所谓面向对象有别于面向过程,面向对象是只需对象之间的交互即可完成任务,但是面向过程的话,需要我们将每一个步骤都详细地做出来.比如,以洗衣服为例,如果是

  • Java杂谈之重复代码是什么

    目录 方法为何要有参数? 长参数列表的问题 解决方案 聚沙成塔 动静分离 告别标记 总结 有经验的程序员应该都见过,一个方法坐拥几十上百个参数. 方法为何要有参数? 因为不同方法间需共享信息. 但方法间共享信息的方式不止一种,除了参数列表,还有全局变量.但全局变量总能带来意外惊喜,所以,取消全局变量也是各大语言的趋势. 但方法之间还是要传递信息的,不能用全局变量,于是参数就成了唯一选择,于是,只要你想到有什么信息要传给一个方法,就会直接它加到参数列表中,参数列表也越来越长. 长参数列表的问题 参

  • Java杂谈之如何优化写出漂亮高效的代码

    目录 命名中的不一致 方案中的不一致 代码中的不一致 总结 大部分程序员对于一致性本身的重要性是有认知的.但通常来说,大家理解的一致性都表现在比较大的方面,比如,数据库访问是叫 DAO还是叫 Mapper,Repository?在一个团队内,这是有统一标准的,但编码的层面上,要求往往就不是那么细致了.所以,我们才会看到在代码细节上呈现出了各种不一致.我们还是从一段具体的代码来分析问题. 命名中的不一致 有一次,我在代码评审中看到了这样一段代码: enum DistributionChannel

  • Java杂谈之代码重构的方法多长才算长

    目录 多长算"长"? 长函数的产生 以性能为由 平铺直叙 一次加一点 总结 每当看到长函数,我们都得: 被迫理解一个长函数 在一个长函数中,小心翼翼地找出需要的逻辑,按需求微调 几乎所有程序员都会有类似经历. 没人喜欢长函数,但你却要一直和各种长函数打交道. 几百上千行的函数肯定是不足以称霸的. 多长算"长"? 100 行?对于函数长度容忍度太高了!这是导致长函数产生的关键点. 看具体代码时,一定要能够看到细微之处.关键点就是将任务拆解得越小越好,这个观点对代码同样

  • Java代码中4种字符串拼接方式分析

    目录 结论 最佳实践 分析过程 环境 分析用示例代码: 代码及结果分析 本文研讨的字符串拼接方式为以下4种:“+”号.StringBuilder.StringJoiner.String#join,对比分析及探讨最佳实践. 结论 后面内容比较枯燥,所以先说结论: 本文研讨的字符串拼接方式为以下4种:“+”号.StringBuilder.StringJoiner.String#join 在简单的字符串拼接场景中「如:"a" + "b" + "c"」,

  • java异步写日志到文件中实现代码

    java异步写日志到文件中详解 实现代码: package com.tydic.ESUtil; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.io.InputStream; import java.io.PrintWriter; import java.util.Properties; public class LogWriter { // 日志的配置文件 publi

随机推荐