避免使用FOR –反假战役

您是否想知道FOR如何影响您的代码? 他们如何限制您的设计,更重要的是如何将您的代码转换为无人为含义的多行代码?

在这篇文章中,我们将看到如何将for的简单示例(由Francesco Cirillio提供- 反if活动)转换为更具可读性和精心设计的示例。

因此,让我们从使用FOR的原始代码开始:

public class Department {

  private List<Resource> resources = new ArrayList<Resource>();

  public void addResource(Resource resource) {
   this.resources.add(resource);
  }

  public void printSlips() {

   for (Resource resource : resources) { 
    if(resource.lastContract().deadline().after(new Date())) { 

     System.out.println(resource.name()); 
     System.out.println(resource.salary());
    }
   }
  }

 }

请参见printSlips方法。 如此简单的方法,只有10条线计数白线,但违反了最重要的规则之一,该方法在其内部混合了不同级别的抽象而不只是做一件事。

正如罗伯特·C·马丁(Robert C. Martin)在他的著作《函数应该做的一件事》中指出的那样 他们应该做得很好。 他们应该只做[…]。 如果一个函数仅执行比该函数的指定名称低一级的步骤,则该函数正在做一件事[…]。

因此,根据给定的方法外观定义,让我们回顾一下以前的方法,看看有多少事情在做
printSlips方法? 具体四个

public void printSlips() {

   for (Resource resource : resources) { #Cycle
    if(resource.lastContract().deadline().after(new Date())) { #Selection
     #Media 
     System.out.println(resource.name()); #Content
     System.out.println(resource.salary());
    }
   }
 }

该方法是循环,选择资源,访问内容以及访问媒体。 看到它们每个都属于不同的抽象级别,打印到控制台应该处于不同的级别,以检查资源是否尚未过期。

让我们看看Francesco提出的解决方案。

首先要做的是将主要功能分为三个类和两个接口,一个用于迭代资源,另一个用于选择尚未过期的资源,另一个用于打印资源。 通过这种方法,我们正在创建一个旨在扩展的解决方案,并且还提高了可读性。

现在是时候编写代码了:

如果资源满足实现条件,则将使用谓词接口来实现。

public interface Predicate {

  boolean is(Resource each);

 }

例如,在我们的例子中,接口的实现如下所示:

public class InForcePredicate implements Predicate {

  public boolean is(Resource each) {
   return each.lastContract().deadline().after(new Date());
  }
 }

我们将条件转移到InForcePredicate类。 请注意,如果我们要创建一个检查合同是否到期的类,我们将创建一个实现Predicate的新类,其内容类似于return each.lastContract()。deadline()。before(new Date())

下一个接口是Block接口,它将实现对媒体的访问。 在这种情况下,要进行控制台:

public interface Block {

  void evaluate(Resource resource);

 }

及其实现:

public class PrintSlip implements Block {

  public void evaluate(Resource resource) {
   System.out.println(resource.name()); 
   System.out.println(resource.salary());
  }

 }

再次注意,更改信息的发送位置(控制台,文件,网络等),只需实现Block接口即可。

最后一个类是一个包含资源迭代器的类,还提供了调用先前创建的每个接口的方法:

public class ResourceOrderedCollection {
  private Collection<Resource> resources = new ArrayList<Resource>();

  public ResourceOrderedCollection() {
   super();
  }

  public ResourceOrderedCollection(Collection<Resource> resources) {
   this.resources = resources;
  }

  public void add(Resource resource) {
   this.resources.add(resource);
  }

  public void forEachDo(Block block) {
   Iterator<Resource> iterator = resources.iterator();

   while(iterator.hasNext()) {
    block.evaluate(iterator.next());
   }

  }

  public ResourceOrderedCollection select(Predicate predicate) {

   ResourceOrderedCollection resourceOrderedCollection = new ResourceOrderedCollection();

   Iterator<Resource> iterator = resources.iterator();

   while(iterator.hasNext()) {
    Resource resource = iterator.next();
    if(predicate.is(resource)) {
     resourceOrderedCollection.add(resource);
    }
   }

   return resourceOrderedCollection;
  }
 }

请参阅接下来的三个要点:

  • 第一个是构造函数接收资源列表。
  • 第二个是select方法接收一个谓词,该谓词将被执行到迭代器中,以知道是否可以选择打印资源。 最后,返回带有资源且没有到期合同的ResourceOrderedCollection的新实例。
  • 第三个forEachDo方法接收一个Block接口,该接口由资源列表的每个元素调用。

最后使用以前开发的类修改了Department类:

public class Department {

  private List<Resource> resources = new ArrayList<Resource>();

  public void addResource(Resource resource) {
   this.resources.add(resource);
  }

  public void printSlips() {
   new ResourceOrderedCollection(this.resources).select(new InForcePredicate()).forEachDo(new PrintSlip());
  }

 }

注意,现在printSlips方法包含具有相同抽象级别的单个可读行。

请注意,类名和接口名取自Francesco的示例,但是如果我也要这样做,则应选择更多具有代表性的名称。 Cirillo的方法不错,但需要考虑一些小方面。 例如,它具有“ 垂直问题 ”: Predicate接口中的InForcePredicate实例使用五行源代码来封装单个语句。

我们探索了问题的两种可能解决方案,这是Cirillio提出的最后一种解决方案。 对于此问题,还有许多其他可能且正确的解决方案,例如,使用“ 模板方法模式” ,或混合使用Lambdaj和(或不使用) 闭包( Lambdaj语法可能会有些混乱)。 它们都有优点和缺点,但是它们都使您的代码更具可读性,而且更重要,所有功能都只能做一件事情,它们只能做得很好。

作为这篇文章的最后说明, JDK 8将提供对本机关闭的支持,还将提供Lambdaj现在提供的许多功能。 同时, JDK 8不稳定(计划于2013年中期完成)或您的旧代码(从JDK 8的角度来看) 不稳定Lambdaj确实是一个很好的同伴。

我们不断学习。

参考: 避免FORs –我们的JCG合作伙伴 Alex Soto的“ 反若战役”,来自“ One Jar to Rule Allm All”博客。

翻译自: https://www.javacodegeeks.com/2012/11/avoiding-fors-anti-if-campaign.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值