Search code examples
javainterfacedry

How to generalize a comparison in Java code


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.


Solution

  • 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;
    }