英文:
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 -> earthquake.magnitude() > minimumMagnitude &&
earthquake.magnitude() < maximumMagnitude;
case INCLUSIVE -> earthquake.magnitude() >= minimumMagnitude &&
earthquake.magnitude() <= 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<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;
}
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<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;
}
答案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
值(例如long
和int
),那么您要么需要提取单独的DoubleComparisonStrategy
,LongComparisonStrategy
枚举类,要么在“主”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 >= 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);
}
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. long
s and int
s 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<T>
into this mix when going with enum
approach.
UPD: combining with the approach from the other answer, this makes:
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);
}
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 -> ComparisonStrategy.INCLUSIVE.test(e.magnidute(), min, max);
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论