Java - Осуществляет рефакторинг два почти идентичных метода

У меня есть два метода, тот, который считает количество объектов, которые, как полагают, имеют нижнее значение, чем данный объект и другого, который считает количество объектов, которые имеют более высокое значение, чем данный объект. Как можно, вероятно, сказать, эти два метода практически идентичны:

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
}

Используя булевскую переменную является также неудовлетворительным, с тех пор в будущем довольно возможно, что я могу хотеть считать количество объектов равного значения.

У Вас есть альтернативное решение для рефакторинга?

Удачи,

Pete

6
задан 3 July 2010 в 13:19
поделиться

8 ответов

Что бы я сделал:

  • Реализуйте компаратор для типа.
  • Передайте экземпляры этого Comparator вместо параметра int compare .
  • Один из счетчиков заключит Comparator в Collections.reverseOrder.

Это позволит вам правильно разделить проблемы.

9
ответ дан 8 December 2019 в 15:58
поделиться

Я согласен - прямое сравнение с целым числом ненадежно, поскольку результаты сравнения гарантированно возвращаются, например, меньше нуля, а не конкретно -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.

4
ответ дан 8 December 2019 в 15:58
поделиться

Используйте 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();
}

Если вы хотите расширить это для проверки равенства, вы просто добавите дополнительное значение в перечисление.

3
ответ дан 8 December 2019 в 15:58
поделиться

Аргументом должен быть Компаратор ( http://java.sun.com/j2se/1.5.0/docs/api/java/util/Comparator.html ) Затем вы можете вызвать метод с анонимным классом, чтобы избежать явной реализации Comparator, если он вам нужен только в одном месте. Если метод становится еще более сложным, вы можете определить свой собственный интерфейс, который инкапсулирует более сложную логику, например, логику внутри самого оператора if.

Также, если вас попросят проголосовать за реализацию labmdas (замыканий) на языке Java:)

0
ответ дан 8 December 2019 в 15:58
поделиться

Вы можете использовать перечисление вместо int . Кроме того, обычно a.compareTo (b) может возвращать произвольное отрицательное число, когда a меньше, чем b , и произвольное положительное число, когда a больше, чем b .

Вот мое решение:

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;
}
0
ответ дан 8 December 2019 в 15:58
поделиться

Я согласен со многими другими; объект типа Predicate здесь правильный, но, в частности, вы хотите вызвать точку mdma, предполагающую, что использование результата int для Comparable.compareTo () неверно (или, по крайней мере, хрупкое) - гарантируется только то, что Comparable возвращает положительное, 0 или отрицательное число, конкретные значения 1 и -1 не диктуются (и многие реализации не повторяют эти ). Опять же, я думаю, что предикат - это то, что вам нужно, но в качестве обходного пути к этой проблеме и риску того, что люди перейдут в нестандартное int, вы можете сделать:

/**
 * ...
 * @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;
}

использование signum означает, что вы всегда сравниваете значения -1,0, 0 или 1,0.

Опять же, я бы не рекомендовал этот подход (думаю, что он делает API очень неочевидным для начала), но решил выложить его здесь из любопытства.

0
ответ дан 8 December 2019 в 15:58
поделиться

Большинство ответов здесь рассмотрели ваши варианты, но я собираюсь добавить еще один: лучшую структуру данных.

В зависимости от характера ваших данных, SortedSet или более расширенный NavigableSet могут лучше служить вашим целям. В частности, представляют интерес эти 3 метода:

headSet (E toElement, логическое значение включительно) : возвращает представление части этого набора, элементы которой меньше (или равны, если включительно ] верно) toElement .

tailSet (E fromElement, логическое значение включительно) : Возвращает представление части этого набора, элементы которого больше (или равны, если включительно истинно) fromElement .

subSet (E fromElement, логическое fromInclusive, E toElement, логическое toInclusive) : возвращает представление части этого набора, элементы которого находятся в диапазоне от fromElement до toElement .

Вот пример использования ( см. Также на 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 , если карта больше подходит, чем набор.

Связанные вопросы

0
ответ дан 8 December 2019 в 15:58
поделиться

Я бы предложил обернуть частный метод и использовать шаблон объекта функции:

Интерфейс для объекта функции:

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){ 
     //...
}
-1
ответ дан 8 December 2019 в 15:58
поделиться
Другие вопросы по тегам:

Похожие вопросы: