Java杂谈之重复代码是什么
作者:JavaEdge. 发布时间:2022-06-08 15:07:24
有经验的程序员应该都见过,一个方法坐拥几十上百个参数。
方法为何要有参数?
因为不同方法间需共享信息。
但方法间共享信息的方式不止一种,除了参数列表,还有全局变量。但全局变量总能带来意外惊喜,所以,取消全局变量也是各大语言的趋势。
但方法之间还是要传递信息的,不能用全局变量,于是参数就成了唯一选择,于是,只要你想到有什么信息要传给一个方法,就会直接它加到参数列表中,参数列表也越来越长。
长参数列表的问题
参数列表过长,你一个 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)。
只有短小的代码,我们才能有更好地把握,而要写出短小的代码,需要我们能够“分离关注点”。
来源:https://blog.csdn.net/qq_33589510/article/details/120423563


猜你喜欢
- 好程序员Java教程分享MyBatis Plus介绍:1.MyBatis Plus 介绍MyBatis Plus 是国内人员开发的 MyBa
- Spring Security提供如下几种认证机制Username & PasswordOAuth2.0 LoginSAML 2.0
- 本文实例讲述了C#简单读取、改变文件的创建、修改及访问时间的方法。分享给大家供大家参考。具体如下:FileInfo fi = new Fil
- 一、MyBatis的增删改查1.1、新增<!--int insertUser();--><insert id="
- 1.static静态变量1.静态变量被同一个类的所有对象共享2.static类变量在类加载的时候就生成使用static保存在class实例的
- 很多时候需要先判断当前用户的网络,才会继续之后的一些处理逻辑。但网络类型获取这一块,我用我自己的的手机调试时遇到一些问题,这里记录一下。一加
- 这里并未涉及到JSR 181 Annotations 的相关应用,具体的三种方式如下① 通过WSDL地址来创建动态客户端 ② 通过服务端提供
- 本文实例讲述了C#中timer定时器用法。分享给大家供大家参考。具体如下:下面的代码通过Timer定时器每隔1000毫秒(1秒)触发一次事件
- 1、HashMap HashMap继承抽象类AbstractMap,实现接口Map、Cloneable, Serializable接口。Ha
- 实例描述现有某班学生的两份成绩,两份成绩中存在一些不一致的记录。需借助于编程方法找出这些不一致的记录。实例代码using System;us
- 归并排序算法思想:分而治之(divide - conquer);每个递归过程涉及三个步骤第一, 分解: 把待排序的 n 个元素的序列分解成两
- 一、概述近期注意到QQ新版使用了沉浸式状态栏,ok,先声明一下效果图:恩,接下来正题。首先只有大于等于4.4版本支持这个半透明状态栏的效果,
- 本文实例讲述了C#检测远程计算机端口是否打开的方法。分享给大家供大家参考。具体分析如下:这段C#代码用于检测远程计算机的3389端口是否处理
- 本文实例为大家分享了Android自定义Banner轮播效果展示的具体代码,供大家参考,具体内容如下自定义View布局<Relativ
- 详解Java读取Jar中资源文件及实现代码 &
- SQLite是Android自带的关系型数据库,是一个基于文件的轻量级数据库。Android提供了3种操作数据的方式,SharedPrefe
- 上图:IntelliJ Idea字符集编码设置步骤详解第一步:第二步:第三步:设置完以下点击OK来源:https://blog.csdn.n
- 本文实例为大家分享了用javaMail实现发送邮件的具体代码,供大家参考,具体内容如下通过javamail发送邮件,代码如下:导入maven
- 本文实例讲述了JFreeChart插件实现的折线图效果。分享给大家供大家参考,具体如下:package com.lei.jfreechart
- 问题在使用 Abp 框架的后台作业时,当后台作业抛出异常,会导致整个程序崩溃。在 Abp 框架的底层执行后台作业的时候,有 try/catc