Дополнительные условия для фильтрации элементов данной коллекции - PullRequest
2 голосов
/ 11 июля 2020

Я хотел бы знать масштабируемый и универсальный c способ применения инкрементного filters к данной коллекции, не теряя причину сбоя, я имею в виду, какой фильтр не соответствует.

Следующий код является упрощением существующего (в нем больше условий и сообщений об ошибках):

public class FailureCause {

  public static void main(String[] args) {
    validateAtRawWay(asList(201, 300, 450));
    validateAtRawWay(asList(20, 30, 40));
    validateAtRawWay(asList(400, 1600));
    validateAtRawWay(asList(10, 25, 36, 201, 1600));
  }

  public static void validateAtRawWay(List<Integer> sourceToCheck) {
    System.out.println("Initial elements: " + sourceToCheck);

    if (!sourceToCheck.stream()
            .filter(FailureCause.upTo100())
            .findAny()
            .isPresent()) {
        System.out.println("ERROR: no numbers less or equals than 100 were found");
        return;
    }
    if (!sourceToCheck.stream()
            .filter(FailureCause.isPerfectSquare())
            .findAny()
            .isPresent()) {
        System.out.println("ERROR: no perfect square numbers were found");
        return;
    }
    List<Integer> validResults = sourceToCheck.stream()
            .filter(FailureCause.upTo100())
            .filter(FailureCause.isPerfectSquare())
            .collect(toList());
    System.out.println("Valid data: " + validResults);
  }

  public static Predicate<Integer> upTo100() {
    return i -> i <= 100;
  }

  public static Predicate<Integer> isPerfectSquare() {
    return i -> {
        double sq = Math.sqrt(i);
        return (sq - Math.floor(sq)) == 0;
    };
  }
}

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

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

public class ConditionToMatch<E> implements Comparable<ConditionToMatch> {

  // To omit getter/setter
  public int order;
  public Predicate<E> condition;
  public boolean wasFound;
  public String errorMessage;

  public ConditionToMatch (int order, Predicate<E> condition, boolean wasFound, String errorMessage) {
    this.order = order;
    this.condition = condition;
    this.wasFound = wasFound;
    this.errorMessage = errorMessage;
  }

  @Override
  public int compareTo(ConditionToMatch o) {
    return ofNullable(o)
            .map(ext -> Integer.compare(order, ext.order))
            .orElse(-1);
  }
} 

Добавление этого варианта использования к предыдущему FailureCause классу:

public class FailureCause {

  public static void main(String[] args) {
    ...

    validateWithConditionClass(asList(201, 300, 450));
    validateWithConditionClass(asList(20, 30, 40));
    validateWithConditionClass(asList(400, 1600));
    validateWithConditionClass(asList(10, 25, 36, 201, 1600));
  }
  ...

  public static void validateWithConditionClass(List<Integer> sourceToCheck) {
    System.out.println("Initial elements: " + sourceToCheck);

    ConditionToMatch<Integer> isUpTo100Condition = isUpTo100ConditionToMatch();
    ConditionToMatch<Integer> isPerfectSquareCondition = isPerfectSquareConditionToMatch();

    // Used to get the "most important" filter has failed
    PriorityQueue<ConditionToMatch> priorizeValidations = new PriorityQueue<>(asList(isUpTo100Condition, isPerfectSquareCondition));

    List<Integer> validResults = sourceToCheck.stream()
            .map(s -> {
                if (isUpTo100Condition.condition.test(s)) {
                    isUpTo100Condition.wasFound = true;
                }
                if (isPerfectSquareCondition.condition.test(s)) {
                    isPerfectSquareCondition.wasFound = true;
                }
                return s;
            })
            .filter(isUpTo100Condition.condition)
            .filter(isPerfectSquareCondition.condition)
            .collect(toList());

    if (validResults.isEmpty()) {
        Iterator<ConditionToMatch> value = priorizeValidations.iterator();
        // To know the first "most important" filter that failed
        while (value.hasNext()) {
            ConditionToMatch current = value.next();
            if (!current.wasFound) {
                System.out.println(current.errorMessage);
                break;
            }
        }
    }
    else {
        System.out.println("Valid data: " + validResults);
    }
  }
  ...  

  public static ConditionToMatch<Integer> isUpTo100ConditionToMatch() {
    return new ConditionToMatch<>(1, FailureCause.upTo100(), false, "ERROR: no numbers less or equals than 100 were found");
  }

  public static ConditionToMatch<Integer> isPerfectSquareConditionToMatch() {
    return new ConditionToMatch<>(2, FailureCause.isPerfectSquare(), false, "ERROR: no perfect square numbers were found");
  }
}

Мой вопрос: есть ли лучший и масштабируемый способ сделать это?

Ответы [ 2 ]

2 голосов
/ 11 июля 2020

Я бы адаптировал ваш первый подход и избегал там потоков.

import static java.util.function.Predicate.not;
...

public static void validateAtRawWay(List<Integer> sourceToCheck) {
    List<Integer> copy = new ArrayList<>(sourceToCheck);
    System.out.println("Initial elements: " + copy);
    
    copy.removeIf(not(upTo100()));
    if (copy.isEmpty()) {
        System.out.println("ERROR: no numbers less or equals than 100 were found");
        return;
    }
    
    copy.removeIf(not(isPerfectSquare()));
    if (copy.isEmpty()) {
        System.out.println("ERROR: no perfect square numbers were found");
        return;
    }
    
    List<Integer> validResults = copy;
    System.out.println("Valid data: " + validResults);
}

Я не знаю, сколько условий вам нужно проверить. Даже если вам придется часто повторять этот пример, я думаю, что он более разборчивый. Вы также можете извлечь метод, который принимает список и предикат, чтобы уменьшить количество повторяющихся блоков.

2 голосов
/ 11 июля 2020

Вы можете сделать это проще, установив логическое значение непосредственно в классе ConditionToMatch:

public class ConditionToMatch<E> implements Predicate<E> {
  public Predicate<E> condition;
  public boolean neverMatchedAnything = true;
  public String errorMessage;

  public ConditionToMatch (Predicate<E> condition, int order, String errorMessage) {
    this.order = order;
    this.condition = condition;
    this.errorMessage = errorMessage;
  }

  @Override
  public boolean test(E e) {
     boolean value = condition.test(e)
    if (value) {
      neverMatchedAnything = false;
    }
    return value;
  }
}

Затем вы можете написать общую c проверку:

public static void validateWithConditionClass(List<Integer> sourceToCheck, List<ConditionToMatch<Integer>> conditions) {
    System.out.println("Initial elements: " + sourceToCheck);
    conditions.sort((c1, c2) -> c2.order - c1.order);

    Stream<Integer> stream sourceToCheck.stream();
    for (ConditionToMatch<Integer> condition : conditions) 
      stream = stream.filter(condition)
    }
    List<Integer> results = streams.collect();

    for (ConditionToMatch<Integer> condition : conditions) 
      if (condition.neverMatchedAnything) {
        System.out.println(current.errorMessage);
        break;
      }
    }
  }

И Speci c реализация для ваших 2 чекеров:

public static void validateWithConditionClass(List<Integer> sourceToCheck) {
    List<ConditionToMatch<Integer>> conditions = Arrays.toList({isUpTo100ConditionToMatch(), isPerfectSquareConditionToMatch()});
    validateWithConditionClass(sourceToCheck, conditions);
  }

И, конечно, функции для создания условий:

  public static ConditionToMatch<Integer> isUpTo100ConditionToMatch() {
return new ConditionToMatch<>(FailureCause.upTo100(), 1, "ERROR: no numbers less or equals than 100 were found");
  }

  public static ConditionToMatch<Integer> isPerfectSquareConditionToMatch() {
return new ConditionToMatch<>( FailureCause.isPerfectSquare(), 2, "ERROR: no perfect square numbers were found");
  }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...