3
interface Resolver {
    boolean canResolve(SomeInput input);

    SomeOutput resolve(SomeInput input);
}

public static void main(String[] args) {
    List<Resolver> resolvers = ...;
    SomeInput someInput = ...;

    SomeOutput someOutput = resolvers
        .stream()
        .filter(res -> res.canResolve(someInput))
        .findFirst()
        .map(res -> res.resolve(someInput))
        .orElseThrow(() -> new IllegalStateException("No suitable handler found."));
}

This solution is inspired by HttpMessageConverter interface in Spring. I'm not sure if there is any official name for this pattern.

Is it a bad practice to have such coupling (resolve should be called ifcanResolve returned true) between two methods of an interface? Is it acceptable if these resolvers are guaranteed to be used only from one place?

Other option I considered is to have one interface method only returning Optional, it would return empty Optional if it can't handle the input. This eliminates coupling but on the usage side it's not as readable as the previous one.

interface Resolver {
    Optional<SomeOutput> resolve(SomeInput input);
}

A third option could be Chain of Responsibility design pattern but that seems a bit overkill for this simple thing.

Does the first option have any other drawback? Is there any other solution that nicely fits this use case?

1
  • 1
    Likely it doesn't apply here, but it's worth noting that a separate "can I do this?" method can expose you to a Time of check time of use attack in specific situations.
    – John Wu
    Commented Oct 22, 2021 at 22:21

4 Answers 4

7

In the context of a single resolver, a separate method to query the interface for its capability is a code smell. Expanding the context to a collection of potential resolvers where we only expect some of them to be capable of handling the input changes the conversation — we expect failures.

There are a variety of ways to handle this, many of which you have already outlined. Personally, I'm torn between a separate "can I handle this" method and returning an Optional<T>. What does "optional" mean in the context of resolvers? You can eliminate this ambiguity with Javadoc comments that document the method call, and what the return value means.

I would be surprised if a method named "resolve" returned an Optional<T>, but not surprised if the method was named tryResolve or resolveIfSupported. If returning an optional, consider renaming the method to reflect the uncertainty of getting a resolver back.

If the method should remain named resolve then I expect it to throw an exception if the input could not be resolved, and as a consumer of that interface I would also want a "can resolve" method that allows me test the object and avoid an exception.

However, I would prefer an Optional<T> for a return value, a better method name that indicates I should expect non-exception failures, and Javadoc comments explaining how to treat the return value. This allows consumers of the interface to:

  • Avoid bone-headed exceptions caused by not checking before calling resolve

  • Clearly understand that they might not always get the desired output

  • Understand what it means when they do not get the desired output, so they can gracefully fall back to something else (for instance, checking the next resolver).

Use cases like this are exactly what Optional<T> was meant for, but choose the method name wisely, and add some documentation about what it means when an empty Optional is returned.

2
  • 1
    Merging the two methods also avoids duplicative processing: in the case where it can be resolved, the two method approach requests processing of someInput twice.
    – Erik Eidt
    Commented Oct 23, 2021 at 15:51
  • +1 for merging the methods, in elimination of programming errors by failing to use the protocol of using canResolve before invoking resolve. Ideally, "protocols" like this are designed to be enforced at compile time; this one is not.
    – Erik Eidt
    Commented Oct 23, 2021 at 15:54
1

It depends. Does your UI change depending on the outcome? For example your app is supposed to convert documents, but some cannot be converted, and for those the “Converrt” button needs to be grayed out - you want a “canConvert” method.

Without such considerations, I’d usually prefer a method “convert” which tries to convert, and reports failures. And in some cases, if the actual conversion is cheap, I’ve implemented “canConvert” by calling “convert” and checking for errors.

Now both “convert” snd “canConvert” may both be expensive. Say you want to convert “War and Peace” and on page 1100 there is some invalid Unicode - then you may change “canConvert” to a quick check, which can be used in the UI, but doesn’t guarantee success. What is required: “canConvert” may only return “false” if “convert” fails, but may return “true” when “convert” fails.

And then you will have operations that have side effects you can’t easily undo. You really, really want methods “canRaiseFireAlarm” and “raiseFireAlarm” to be separate.

1
  • +1 It depends on what you want to NOT do, and how much you are willing to pay for code that doesn't do it. Your fire alarm example is great. Reminds me of Ian Malcom from Jurassic Park: "Yeah, yeah, but your scientists were so preoccupied with whether or not they could that they didn't stop to think if they should."
    – Cort Ammon
    Commented Oct 25, 2021 at 20:47
0

In a typical object interface this would be odd because the object implements the interface, thereby declaring it can handle calls to the published methods. If the implementation gets into trouble processing a call it can throw or return an error code, there is no need for a separate method.

In another context this is not uncommon though: driver interfaces that need to provide a standard facade to a variety of hardware can have methods that allow the client to query the hardware's capability and thus the interface's usefulness.

0

It depends mostly on how generic will this Resolver usage be. If its scoped to be used passing in a set of types on which you have control over, the method resolve should be receiving only a Resolvable object. This is, you know it can be resolved before hand. This way you delegate the checking responsibility to the own type's creation ceremony, being a factory method, constructor and so on.

It's a different thing though if your interface method should be accepting any object whose type is resolved at runtime. Here, I would keep the canResolve() and resolve() methods, the latter throwing an exception if something goes wrong.

Not the answer you're looking for? Browse other questions tagged or ask your own question.