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.
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.
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With