If there may exist an object with a method that is not appropriate at all points in the existence of the object, then the object or the method are flawed.
A class encapsulating a compile phase in an IDE might have a blocking or non-blocking
execute() method, plus a
getErrorMessages(). There is an obvious protocol in using this class - instantiate, call
getErrorMessages(). It's not particularly hard to use, though it's also not hard to get wrong. Even if you decide not to help those users who don't bother to learn the protocol, it's worth thinking about whether that protocol should even exist, and what the alternative is.
Many readers would probably, when prompted at least, make
execute() return the error messages (or a
Future for them), which solves the problem quite well. If you wouldn't, keep adding phases plus methods only appropriate for each phase, to one class that grows and grows, until you end up agreeing or changing career :) Anyway, in this case it's clear that the object was flawed by doing two things one after the other - executing the compile phase and delivering results.
I bet most people could train themselves to spot this flaw and remove it, and if anyone only goes that far as a result of reading this post I'll be happy. But most people are probably quite happy with another flaw,
java.util.Iterator.next(), which is only allowed when
true, in most
Iterator implementations. But moving
hasNext() onto another object doesn't really work for
Iterator. For a long time I was unhappy with
Iterator, but didn't really have a solution, despite trying a couple of things out.
The biggest use of
Iterator directly in Java for many years was in what has since been replaced by a foreach loop. There are some detractors of, well, anything new, but generally the foreach loop was really well received by the Java community. It provides a higher-level interface than the
Iterator gives us. We can write a lot of code using the foreach loop that would have been more verbose and awkward to get right using
Iterator directly. But foreach is only one abstraction; there are some more that are higher still than it, and don't (but can) depend on
Iterator. If you don't know what those abstractions are I really think you should take the time to learn about map, filter and reduce, and the more general but less usable parent of those, fold. But this isn't a post about those, so I'll return to the topic at hand.
Iterator has been shown to be flawed, though flawed in a way that is acceptable to most of us and in a way we're used to, and in a way that seems non-trivial to solve (without knowing about map, filter, reduce and fold!). You might not be in a position to, or even want to, replace
Iterator, but at least you should know not to copy its design, or bind yourself unnecessarily to it. You're now either armed with a simple way of deciding between two API designs, or you're about to tell me why I'm wrong.