如何在Java代码中进行比较的泛化。

huangapple go评论68阅读模式
英文:

How to generalize a comparison in Java code

问题

我有一个Filter接口,其中可以有不同的实现(以下是其中之一):

public final class MagnitudeFilter implements Filter {

    private final double minimumMagnitude;
    private final double maximumMagnitude;
    private final ComparisonStrategy strategy;

    public MagnitudeFilter(double minimumMagnitude, double maximumMagnitude, ComparisonStrategy strategy) {
        this.minimumMagnitude = minimumMagnitude;
        this.maximumMagnitude = maximumMagnitude;
        this.strategy = strategy;
    }

    @Override
    public boolean satisfies(Earthquake earthquake) {
        return switch (strategy) {
            case EXCLUSIVE -> earthquake.magnitude() > minimumMagnitude &&
                earthquake.magnitude() < maximumMagnitude;
            case INCLUSIVE -> earthquake.magnitude() >= minimumMagnitude &&
                earthquake.magnitude() <= maximumMagnitude;
        };
    }
}

有时我想执行“包含”的比较,而其他时候我想执行“排除”的比较,但是在上面的形式中,我需要为每个单独的实现重复switch表达式中的代码。是否有一种替代方法可以将此模式泛化为所有实现,以便不必在switch语句中重复代码?

尝试使用另一个接口,但最终又陷入了重复代码的循环中。

英文:

Say I have a Filter interface

public interface Filter {
   public boolean satisfies(Earthquake earthquake)
}

Which can have different implementations (one such below)

public final class MagnitudeFilter implements Filter {

    private final double minimumMagnitude;
    private final double maximumMagnitude;
    private final ComparisonStrategy strategy;

    public MagnitudeFilter(double minimumMagnitude, double maximumMagnitude, ComparisonStrategy strategy) {
        this.minimumMagnitude = minimumMagnitude;
        this.maximumMagnitude = maximumMagnitude;
        this.strategy = strategy;
    }

    @Override
    public boolean satisfies(Earthquake earthquake) {
        return switch (strategy) {
            case EXCLUSIVE -&gt; earthquake.magnitude() &gt; minimumMagnitude &amp;&amp;
                earthquake.magnitude() &lt; maximumMagnitude;
            case INCLUSIVE -&gt; earthquake.magnitude() &gt;= minimumMagnitude &amp;&amp;
                earthquake.magnitude() &lt;= maximumMagnitude;
        };
    }
}

In some cases I would like to perform an inclusive comparison and other times I would like to perform an exclusive comparison, however in the above form, I will need to repeat the switch expression for every single implementation.

Is there an alternative way that I can generalize this pattern for all implementations so as not to have to repeat the code in the switch statement?

Tried to user another interface but ended up in a loop of repeating code again.

答案1

得分: 3

请注意,您的 Filter 基本上与 java.util.function.Predicate 完全相同,因此我们可以直接使用它。您也不需要 Filter 的子类;只需使用lambda表达式。

在您的示例中,包含/排除的选择与过滤器一起传递,因此您只需定义两个工厂:

Predicate<Earthquake> rangeInclusive(double min, double max) { 
    return e -> e.magnitude() > min && e.magnitude() <= max;
}

Predicate<Earthquake> rangeExclusive(double min, double max) { 
    return e -> e.magnitude() > min && e.magnitude() < max;
}

如果您希望使用数据而不是方法名称来选择包含/排除,您可以在一个地方使用switch实现:

Predicate<Earthquake> range(double min, double max, ComparisonStrategy strat) { 
    return switch (strat) { 
        case INCLUSIVE -> e -> e.magnitude() > min && e.magnitude() <= max;
        case EXCLUSIVE -> e -> e.magnitude() > min && e.magnitude() < max;
    }
}

这些是您提供的代码部分的翻译。

英文:

Note that your Filter is basically identical to java.util.function.Predicate, so we can just use that. You also don't need a subclass of Filter; just use lambdas.

In your example, the inclusive/exclusive choice travels with the filter, so all you have to do is define two factories:

Predicate&lt;Earthquake&gt; rangeInclusive(double min, double max) { 
    return e -&gt; e.magnitude() &gt; min &amp;&amp; e.magnitude() &lt;= max;
}

Predicate&lt;Earthquake&gt; rangeExclusive(double min, double max) { 
    return e -&gt; e.magnitude() &gt; min &amp;&amp; e.magnitude() &lt; max;
}

If you want to use data, rather than the method name, to select inclusive/exclusive, you can do that with the switch in just one place:

Predicate&lt;Earthquake&gt; range(double min, double max, ComparisonStrategy strat) { 
    return switch (strat) { 
        case INCLUSIVE -&gt; e -&gt; e.magnitude() &gt; min &amp;&amp; e.magnitude() &lt;= max;
        case EXCLUSIVE -&gt; e -&gt; e.magnitude() &gt; min &amp;&amp; e.magnitude() &lt; max;
}

答案2

得分: 0

似乎,您的代码的一般部分是关于比较是INCLUSIVE还是EXCLUSIVE,而包装器Filter主要用于属性提取。

如果我理解正确,那么可以这样做:

public enum ComparisonStrategy {
  INCLUSIVE() {
    @Override
    public boolean test(double value, double min, double max) {
      return value >= min && value <= max;
    }
  },
  EXCLUSIVE() {
    @Override
    public boolean test(double value, double min, double max) {
      return value > min && value < max;
    }
  };

  public abstract boolean test(double value, double rangeStart, double rangeEnd);
}

这样您的过滤器变为:

public final class MagnitudeFilter implements Filter {

  private final double minimumMagnitude;
  private final double maximumMagnitude;
  private final ComparisonStrategy strategy;

  public MagnitudeFilter(
      double minimumMagnitude, double maximumMagnitude,
      ComparisonStrategy strategy) {
    this.minimumMagnitude = minimumMagnitude;
    this.maximumMagnitude = maximumMagnitude;
    this.strategy = strategy;
  }

  @Override
  public boolean satisfies(Earthquake earthquake) {
    return strategy.test(
      earthquake.magnitude(), minimumMagnitude, maximumMagnitude
    );
  }
}

这种方法的明显缺点是,如果您的ComparisonStrategy用于测试不仅仅是double值(例如longint),那么您要么需要提取单独的DoubleComparisonStrategyLongComparisonStrategy枚举类,要么在“主”ComparisonStrategy类中创建许多方法,如boolean testDouble(double, double, double)boolean testLong(long, long long)等。当采用enum方法时,甚至可能无法为您想要比较的所有可能属性定义适当的比较 - 例如,当使用Comparator<T>时,将其包含在此混合中会更加困难。

更新:结合来自其他答案的方法,可以这样做:

Predicate<Earthquake> magnitudeInclusive(double min, double max) {
  return e -> ComparisonStrategy.INCLUSIVE.test(e.magnitude(), min, max);
}

Predicate<Earthquake> magnitudeExclusive(double min, double max) {
  return e -> ComparisonStrategy.EXCLUSIVE.test(e.magnitude(), min, max);
}

如果您仍然想要使用Filter接口,那么这也将是一样的,因为它也是有效的lambda目标:

Filter magnitudeInclusive(double min, double max) {
  return e -> ComparisonStrategy.INCLUSIVE.test(e.magnitude(), min, max);
}
英文:

Seems to me, that the general part of your code is whether the comparison is INCLUSIVE or EXCLUSIVE, while the wrapper Filter is mostly for the property extraction.

If I'm correct, then what about doing this:

public enum ComparisonStrategy {
  INCLUSIVE() {
    @Override
    public boolean test(double value, double min, double max) {
      return value &gt;= min &amp;&amp; value &lt;= max;
    }
  },
  EXCLUSIVE() {
    @Override
    public boolean test(double value, double min, double max) {
      return value &gt; min &amp;&amp; value &lt; max;
    }
  };

  public abstract boolean test(double value, double rangeStart, double rangeEnd);
}

So your filter becomes:

public final class MagnitudeFilter implements Filter {

  private final double minimumMagnitude;
  private final double maximumMagnitude;
  private final ComparisonStrategy strategy;

  public MagnitudeFilter(
      double minimumMagnitude, double maximumMagnitude,
      DoubleComparisonStrategy strategy) {
    this.minimumMagnitude = minimumMagnitude;
    this.maximumMagnitude = maximumMagnitude;
    this.strategy = strategy;
  }

  @Override
  public boolean satisfies(Earthquake earthquake) {
    return strategy.test(
      earthquake.magnitude(), minimumMagnitude, maximumMagnitude
    );
  }
}

Obvious downside of this approach is if your ComparisonStrategy is used to test for more than just double values (i.e. longs and ints too) - then you either will need to extract separate DoubleComparisonStrategy, LongComparisonStrategy enum classes, or make many methods like boolean testDouble(double, double, double), boolean testLong(long, long long), etc. in the "main" ComparisonStrategy class. It may not even be possible to define a proper comparison for all possible properties you would want to compare - i.e. it's going to be a lot harder to include a Comparator&lt;T&gt; into this mix when going with enum approach.

UPD: combining with the approach from the other answer, this makes:

Predicate&lt;Earthquake&gt; magnitudeInclusive(double min, double max) {
  return e -&gt; ComparisonStrategy.INCLUSIVE.test(e.magnitude(), min, max);
}

Predicate&lt;Earthquake magnitudeExclusive(double min, double max) {
  return e -&gt; ComparisonStrategy.EXCLUSIVE.test(e.magnitude(), min, max);
}

Also, this will look the same if you still want to use Filter interface, because that is also a valid lambda target:

Filter magnitudeInclusive(double min, double max) {
  return e -&gt; ComparisonStrategy.INCLUSIVE.test(e.magnidute(), min, max);
}

huangapple
  • 本文由 发表于 2023年6月8日 20:13:57
  • 转载请务必保留本文链接:https://go.coder-hub.com/76431757.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定