Java - 重构两个几乎相同的方法

时间:2010-07-03 13:19:57

标签: java refactoring

我有两个方法,一个计算被认为具有比给定对象更低值的对象的数量,另一个计算具有比给定对象更高值的对象的数量。你可以说,这两种方法实际上是相同的:

public int countHigher(SomeObject a){
    if (a == null){
           throw etc...
    }
    int numberHigher = 0;
    for (SomeObeject b : this.listOfSomeObjects) {
        if (b.compareTo(a) == 1) {
            numberHigher++;
        }
    }
    return numberHigher;
}

public int countLower(SomeObject a){
    if (a == null){
           throw etc...
    }
    int numberLower = 0;
    for (SomeObeject b : this.listOfSomeObjects){
        if (b.compareTo(a) == -1){
            numberLower++;
        }
    }
    return numberLower;
}

我重构了调用私有方法的方法:

private int coun(SomeObject a, int comparison){
    if (a == null){
           throw etc...
    }
    int number = 0;
    for (SomeObeject b : this.listOfSomeObjects){
        if (b.compareTo(a) == comparison){
            number++;
        }
    }
    return number;
}

但我觉得这个解决方案并不令人满意。可以使用无效的整数(即10)调用私有方法,并且对这种情况进行额外的错误检查是相当丑陋的:

if (comparison < -1 || comparison > 1)
{
    throw blah
}

使用布尔值也不能令人满意,因为将来我可能想要计算相等值的对象数。

您是否有其他重构解决方案?

干杯,

皮特

8 个答案:

答案 0 :(得分:9)

我会做什么:

  • 为该类型实施比较器。
  • 传递该比较器的实例而不是int comparison参数。
  • 其中一个计数器将Comparator包装在Collections.reverseOrder中。

这将为您提供适当的分离问题。

答案 1 :(得分:4)

我同意 - 直接与整数进行比较是脆弱的,因为比较只能保证返回,例如小于零,而不是具体-1。

您可以使用谓词来执行比较。这封装了您想要计算的条件的评估。例如,

   abstract class ComparisonPredicate
   {
       private SomeObject a;
       // set in constructor

       public abstract boolean evaluate(SomeObject b);
   }

   class LessThanPredicate extends ComparisonPredicate
   {

      public boolean evaluate(SomeObject b) {
          return a.compareTo(b)<0;
      }
   }

然后count方法变为:

private int count(ComparisonPredicate comparison){
int number = 0;
for (SomeObeject b : this.listOfSomeObjects){
    if (comparison.evaluate(b)){
        number++;
    }
}
return number;
}

然后调用该方法:

   SomeObject a = ...;
   int lessThanCount = count(new LessThanPredicate(a));    

这可用于查找任何类型评估的对象数量 - 不仅小于/大于比较,还包括相等以及在对象上定义的任何其他关系。

或者,您可以将比较值标准化为-1,0,1。

答案 2 :(得分:3)

使用Enums

public class test {
    private int count(Object a, Comparison c){
        if (a == null){
               throw new RuntimeException();
        }
        int number = 0;
        for (Object b : new Object[] {null, null}){
            if (b.compareTo(a) == c.val()){
                number++;
            }
        }
        return number;
    }

    public void test() {
        System.out.println(count(null, Comparison.GT));
        System.out.println(count(null, Comparison.LT));
    }
}

class Object implements Comparable {
    public int compareTo(java.lang.Object object) {
        return -1;
    }
}

enum Comparison {
    GT { int val() { return 1; } },
    LT { int val() { return -1; } };

    abstract int val();
}

如果你想扩展它以检查equals,你只需要在枚举中添加一个额外的值。

答案 3 :(得分:0)

论证应该是比较者(http://java.sun.com/j2se/1.5.0/docs/api/java/util/Comparator.html) 然后,您可以使用匿名类调用该方法,以避免在一个地方只需要它时显式实现Comparator。如果方法变得更加复杂,您可能需要定义自己的接口来封装更复杂的逻辑,例如if语句本身内部的逻辑。

此外,如果要求您投票支持使用Java语言实现labmdas(闭包):)

答案 4 :(得分:0)

您可以使用enum代替int。此外,通常a.compareTo(b)可以在a小于b时返回任意负数,在a大于b时返回任意正数。< / p>

这是我的解决方案:

private static enum CountMode {CountHigher, CountLower};

private int count(SomeObject anObject, CountMode mode) {
    if (a == null) {
        // handle
    }

    int count = 0;
    for (SomeObject o : this.listOfSomeObjects) {
        int compare = o.compareTo(anObject);
        if (compare > 0 && mode == CountMode.CountHigher) {
            count++;
        } else if (compare < 0 && mode == CountMode.CountLower) {
            count++;
        }
    }

    return count;
}

以下是处理相同案例的修改后的代码:

private static enum CountMode {CountHigher, CountLower, CountEqual};

private int count(SomeObject anObject, CountMode mode) {
    if (a == null) {
        // handle
    }

    int count = 0;
    for (SomeObject o : this.listOfSomeObjects) {
        int compare = o.compareTo(anObject);
        if (compare > 0 && mode == CountMode.CountHigher) {
            count++;
        } else if (compare < 0 && mode == CountMode.CountLower) {
            count++;
        } else if (compare == 0 && mode == CountMode.CountEqual) {
            count++;
        }
    }

    return count;
}

答案 5 :(得分:0)

我同意许多其他人的意见;一个Predicate - 类型的对象在这里是正确的,但是特别想要调出mdma的观点,假设依赖于int的{​​{1}}结果是不正确的(或者至少是脆弱的) - 它只是保证Comparable.compareTo()返回正数,0或负数,特定值1和-1不是必需的(并且许多实现不会重新执行这些)。再一次,我认为Comparable是可行的,但作为解决此问题的方法,以及人们传入Predicate int的风险,您可能会这样做:

non-standard

使用/** * ... * @param int the sign of the comparison; will count objects whose .compareTo(a) * returns an int with the same sign as this value */ private int count(SomeObject a, int comparisonSign){ ... for (SomeObject b : this.listOfSomeObjects){ if (Math.signum(b.compareTo(a)) == Math.signum(comparison)){ number++; } } return number; } 意味着您始终在比较-1.0,0或1.0值。

同样,我不推荐这种方法(认为它使API非常不明显,一开始),但我认为我出于好奇而把它放在这里。

答案 6 :(得分:0)

这里的大多数答案都涵盖了您的选择,但我还要再添加一个:更好的数据结构。

根据您的数据的性质,SortedSet<E>或更丰富的NavigableSet<E>可能会更好地满足您的目的。具体来说,这三种方法很有意义:

  

headSet(E toElement, boolean inclusive):返回此集合部分的视图,其元素小于(或等于,如果inclusive为真)toElement

     

tailSet(E fromElement, boolean inclusive):返回此集合部分的视图,其元素大于(或等于,如果inclusive为真)fromElement

     

subSet(E fromElement, boolean fromInclusive, E toElement, boolean toInclusive):返回此集合部分的视图,其元素范围从fromElementtoElement

以下是一个示例用法(see also on ideone.com):

import java.util.*;
public class NavigableSetExample {
    public static void main(String[] args) {
        NavigableSet<String> names = new TreeSet<String>(
            Arrays.asList(
                "Bob", "Carol", "Alice", "Jill",  "Jack", "Harry", "Sally"
            )
        );

        System.out.println(names);
        // [Alice, Bob, Carol, Harry, Jack, Jill, Sally]

        // "Carol" and up
        System.out.println(names.tailSet("Carol", true));
        // [Carol, Harry, Jack, Jill, Sally]

        // below "Elizabeth"
        System.out.println(names.headSet("Elizabeth", false));
        // [Alice, Bob, Carol]

        // who stands between "Harry" and "Sally"?
        System.out.println(names.subSet("Harry", false, "Sally", false));
        // [Jack, Jill]     
    }
}

如果地图比集合更适合,那么当然有NavigableMap<K,V>

相关问题

答案 7 :(得分:-1)

我建议包装一个私有方法并使用函数对象模式:

功能对象的接口:

interface Filter<T>{
    boolean eligible(T t);
}

计算更高的包装器方法以显示这个想法:

public int countHigher(final SomeObject a) 
{ 
 return coun(a, new Filter<SomeObject>()
           {   
                 public boolean eligible(SomeObject b){
                      return a.compareTo(b) == -1;
                 }
           }); 
} 

计算符合条件的对象的私人帮助方法

private int coun(SomeObject a, Filter<SomeObject> filter){ 
     //...
}