Although I have strived to be succinct in my question, I know the following has become a little long and already thank you for your patience reading it. The questions are not so much about how to implement a particular use case, but rather how to adhere to good OO design principles when it comes to return types and use of exceptions.
I understand the OO principles etc, but are somewhat new to the pragmatic requirements of real-world programming and sometimes find it hard to comply with all design principles.
Like for instance in the example below where the method's returntype only makes sense if the method completes successfully: here i have a 2D Object array, and it only makes sense to return the index if there is a match between the string passed in and one of the [n][0] elements in the matrix (
I have TWO related questions:
QUESTION ONE:
What shall i do with regards to return type? Ideally I should not require the client methods to know of any implementation details, like know that if they get -1 back it means that there wasn't a mach. That is; even require the client to check for the -1 occurence that otherwise would never be normal range when asking for an index of an array.
The way I have solved it here is to have the method throw an exception in case of no match. I am using an exception here as a - well, no so exceptional - branch of the control flow. Basically using it to flag that the outcome of the method was different than the integer, and thus forcing the caller to take appropriate action (prompt the user for a different key or something). Is that good?
QUESTION TWO: In case the way of using exceptions here is ok, I can run into some semantic problems when I try and use it in different contexts:
Use Case A: get item out of the matrix based on a key.
- Here the innate semantic of complementaryIndex makes sense: it returns the matching index to a certain String key, throws an exception if no match is found.
Use Case B: insert item into the matrix.
- this usecase controller could reuse the code in complementaryIndex to check that the key isn't already in the Matrix. Here the semantics break down: complementaryIndex is called for its side effect, namely the NoSuchFieldException to see if its okay to insert my element in the matrix based on the key. In other words, the essential code implementing usecase B is actually placed within a catch block like this:
void insertItem(String key, Object item) {
try { complimentaryIndex(key); // called for its side effect, the NoSuchFieldException
} catch (NoSuchFieldException nsfe) {
// code to insert the item at the end of the array
}
}
Placing the main succes scenario for the use case within a catch block looks a bit like we are handling an error, rather than solving the main use case scenario. So although I avoid duplicating code by reusing the complementaryIndex() method, It is not semantically very clear in its second use. How should I solve this and code it in a meaningful and sensible way?