Suppose there are some methods to convert from "X" to "Y" and vice versa; the conversion may fail in some cases, and exceptions are used to signal conversion errors in those cases.
Which would be the best option for defining exception classes in this context?
- A single
XYConversionException
class, with an attribute (e.g. anenum
) specifying the direction of the conversion (e.g.ConversionFromXToY
,ConversionFromYToX
). - A
XYConversionException
class, with two derived classesConversionFromXToYException
andConversionFromYToXException
. ConversionFromXToYException
andConversionFromYToXException
classes without a common base class.
-
Are these conversions simple? More specifically: is a large amount of composite data types being batch-converted? In the latter case, the result might be partly successful and partly failed. Non-fatal conversion warnings might be needed for issues that are not critical enough to warrant throwing an exception.rwong– rwong2012年10月31日 11:37:13 +00:00Commented Oct 31, 2012 at 11:37
-
@rwong: The conversions are all-or-nothing: either they succeed or they fail. In case of failures, exceptions are thrown.Mr.C64– Mr.C642012年10月31日 11:57:17 +00:00Commented Oct 31, 2012 at 11:57
3 Answers 3
The foremost reason for having different exception types is to be able to catch them selectively.
So the question you should ask yourself is: Will you ever have a piece of code where conversion might fail and you only want to catch conversion errors in one direction and not the other? If so, having two distinct exception classes is the best way to go. If you want to be able to catch both in the same clause, then you need a common super class as well.
If you do find it hard to anticipate how the exceptions are caught, then stick to YAGNI and go with the first option. You can always add the subclasses later if you ever actually need them.
Apart from that, I think you should ask yourself whether exceptions are the right way to signal conversion failure, because in fact failure is an expected option. In fact a lot of APIs merely use sentinel values for that.
Another nice way to propagate errors would be to wrap return information of any call that can fail like this (in pseudo-code):
class Outcome<Result, Error> {
const Bool success;
const Result result;
const Error error;
Result sure() {
if (success) return result;
else throw error;
}
}
Outcome<X, { value: Y, message: String }> convertYToX(Y y) {
if (suitable(y)) return Outcome{ success: true, result: convert(y) };
else return Outcome{ success: false, error: { }};
}
And then either do:
handleX(convertXToY(myY).sure());//will throw an exception if an error occurred
Or inspect the result yourself:
var o = convertXToY(myY);
if (o.success)
handleX(o.result);
else {
log('error occured during conversion:');
log(o.error.message);
log('using default');
handleX(defaultX);
}
-
+1 For not throwing exceptions for expected behavior.Songo– Songo2012年10月31日 16:10:37 +00:00Commented Oct 31, 2012 at 16:10
Don't over-think it,
My suggestions:
Regarding the signature of the constructor, all over the Java API, classes that extend Exception, uses one or more of the following constructors:
1. PrintException() // Construct a print exception with no detail message.
2. PrintException(Exception e) // Construct a print exception chaining the supplied exception.
3. PrintException(String s) // Construct a print exception with the given detail message.
4. PrintException(String s, Exception e) // Construct a print exception with the given detail message and chained exception.
I really think that one or two of this constructors will suffice for you, probably 1 and 3.
Regarding whether or not you should have separate exceptions for X->Y and Y->X, I strongly advice not to do so, because you will end up writing exceptions for Z->X, X->Z, P->Q, Q->P, Q->X, Q->Y and a lot of permutations. You shoul ask what is the added value of this.
-
This should either be a comment or you should actually provide at least some sort of reasoning.back2dos– back2dos2012年10月31日 12:51:37 +00:00Commented Oct 31, 2012 at 12:51
-
@back2dos You are right, I improved my answer.Tulains Córdova– Tulains Córdova2012年10月31日 14:42:14 +00:00Commented Oct 31, 2012 at 14:42
What language? Many languages already have well defined Exception classes. You would just derive from those and add the details.
For something trivial like this, a single ConversionException should be enough, details can go into the class.
As a Ruby programmer I would even consider to just use the default exception class and put the details into the message part it provides under some circumstances.
-
This was C++ related, but I think that's not very important to the question. I think the question is pretty language-agnostic. It's not interesting to know if the exception class is derived from
std::runtime_error
orCAtlException
or something different; the focus is on how to organize the custom exception class(es).Mr.C64– Mr.C642012年10月31日 11:59:34 +00:00Commented Oct 31, 2012 at 11:59