温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

如何理解Java重复代码

发布时间:2021-10-08 10:02:22 来源:亿速云 阅读:152 作者:iii 栏目:开发技术

这篇文章主要介绍“如何理解Java重复代码”,在日常操作中,相信很多人在如何理解Java重复代码问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”如何理解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重复代码”的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注亿速云网站,小编会继续努力为大家带来更多实用的文章!

    向AI问一下细节

    免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

    AI