Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Alternatives for instanceof when processing concrete types in Java

Let's say I have the following class hierarchy:

public interface Result {
}
public class Failure implements Result {
    private String msg;
    public Failure(String msg) {
        this.msg = msg;
    }
    @Override public String toString() { return msg; }
}
public class Success implements Result {
    private int time, count;
    public Success(int time, int count) { this.time = time; this.count = count; }
    public int getTime() { return time; }
    public int getCount() { return count; }
    @Override public String toString() { return time + ", " + count; }
}

which basically defines an interface Result with two concrete classes: Success and Failure.

Then, I have a calculate method that returns a result and based on the result I should do something, for example:

Result result = calculate();

I have several options to figure it out the concrete class. The most obvious one is to use instance of:

if (result instanceof Success) {
    int time = result.getTime();
    int count = result.getCount();
    // Do something with them
} else {
    throw new RuntimeException(result.toString());
}

In programming languages like Scala that support pattern matching, I could use a match like

case Success =>
case Failure =>

but since Java doesn't support it, I'm not sure what's the best to do. I can for example extend the Result interface as follows:

public interface Result {
    public boolean isSuccess();
    public boolean isFailure();
    public Success asSuccess();
    public Failure asFailure();
}

and then write the client code as:

if (result.isSuccess()) {
   Success success = result.asSuccess();
   int time = success.getTime();
   int count = success.getCount();
   // Do something with them
} else {
   throw new RuntimeException(result.toString());
}

but in this case the interface is bound to concrete classes which is not good either.

What is the best way to represent such cases? I really don't want to put the processing code in the Result hierarchy as it really up to the client to get the data and do whatever they want, so polymorphic dispatch solutions may not be the best. Result hierarchy is at best a data type encapsulation, not operation.

like image 922
Wickoo Avatar asked May 23 '14 12:05

Wickoo


2 Answers

IMO, this is an inappropriate usage of "Marker Interface" pattern. You do want to use some functionalities defined by the interface, then don't make it as a Marker. Add methods in. To make it generic, you may want to know the type/category of the Result like Success, Error, Fatal, Warning, etc... you can consider to make an Enum, enum ResultType and in your interface, except for the functional methods (getTime, getCount...) add a getType() or, just use an abstract class Result and declare a protected Type type.

To decide concrete type and do corresponding operation, you can consider to do with case statements on those enum types. But you don't have to cast them to concrete type, because all functionalities you want are defined in the interface/abstract class Result .

sorry for the lack of code example. Not convenient to code right now.

like image 144
Kent Avatar answered Oct 25 '22 00:10

Kent


If there are not only two subtypes, then the visitor pattern would be suitable. Consider:

interface Result {
    // resut of the implementation
    public <T> T accept(Visitor<T> visitor);
}

class Success implements Result {
    @Override
    public <T> T accept(Visitor<T> visitor) {
        return visitor.visit(this);
    }
}

class Failure implements Result {
    @Override
    public <T> T accept(Visitor<T> visitor) {
        return visitor.visit(this);
    }
}

Then create a visitor class:

interface Visitor<T> {
    T visit(Failure failure);
    T visit(Success success);
}

Then where you would use instanceof:

Result result = ...;
// this could be something else, with a different template parameter.
String visitorResult = result.accept(new Visitor<String>() {
    @Override
    public String visit(Failure failure) {
        return "Failure visited";
    }

    @Override
    public String visit(Success success) {
        return "Success visited";
    }
});

This is only my idea of implementation, with generics you can add more customizable features. If you want to read more about this: http://en.wikipedia.org/wiki/Visitor_pattern

like image 45
Balázs Édes Avatar answered Oct 25 '22 00:10

Balázs Édes