Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Implementing classes that should behave as Optional

Imagine finding out if two shapes intersect. An intersection of two shapes may be either another shape, or nothing. If there is no intersects(Shape) method in Shape, then, I believe, the proper object-oriented solution would be:

public final class ShapesIntersection implements Maybe<Shape> {
    public ShapesIntersection(Shape a, Shape b) {
        this.a = a;
        this.b = b;
    }

    @Override
    public boolean isPresent() {
        // find out if shapes intersect
    }

    @Override
    public Shape get() {
        // find the common piece of two shapes
    }
}

In JDK, Optional is a final class, not an interface. To properly solve problems like this one, I'm going to write my own Maybe interface that will look like this:

public inteface Maybe<T> {
    T get();
    boolean isPresent();
    default Optional<T> asOptional() {
       return isPresent() ?
           Optional.of(get()) :
           Optional.empty();
    }
}

What caveats may there be if I stick to this solution implementing Maybe whenever I need optional behavior? Also, this task seems to be quite universal. Am I reinventing the wheel here with introducing my own Maybe interface?

I should add that the whole hassle with a separate class and interface is to omit implementing the behavior using static methods.

like image 800
gvlasov Avatar asked Apr 19 '15 15:04

gvlasov


2 Answers

You are reinventing the wheel here. The reason Optional is final, is because there is really no reason to change it, and the internal semantics require consistency across the usage.

The real issue here is the logic of your constructor. You should not be using a constructor to determine the logic of the intersection. What you want is a (static?) method that performs the calculation for you, and returns the relevant Optional.

public static Optional<Shape> intersection(Shape a, Shape b) {
    // compute if there is an overlap
    if (!checkOverlaps(a,b)) {
        return Optional.empty();
    }
    Shape intersection  = ....
    return Optional.of(intersection);
}

Note that the Optional.empty() and Optional.of(....) are factory methods that create appropriate instances of the Optional. Java 8 streams, functions, and other supporting structures use a number of static factory methods to create instances of these otherwise final classes.

like image 109
rolfl Avatar answered Oct 20 '22 22:10

rolfl


As rolfl said, this is a strange idea. Imagine you want to compute xy for two ints. Sometimes it's undefined, so would you implement a Maybe<Integer>? And then another implementation for e.g. nCr(x, y)?

This sound wrong, doesn't it? The problem is that you're binding the origin of the thing (intersection, power, choose) to the thing itself. But an intersection of two Shapes is nothing but a Shape again (or nothing at all, which can be nicely represented via Optional. Or even better with null; just call me old-school).

The OO aproach makes no sense here, as there's no new kind of object. 22 is exactly the same thing as nCr(4, 1) and both are exactly of the same kind as 4.

Another thing is that you have to call the ShapesIntersection constructor. This is actually a static call, so you may as well write a static helper method instead.

Extending Shape by some IntersectableShape might make sense. There are cases when some operations are common enough for such a thing, see e.g. FluentIterable, but I doubt you'd make that many intersections.

like image 43
maaartinus Avatar answered Oct 21 '22 00:10

maaartinus