Design: How to obtain precise logs when logging from "High-level" classes
Question
The following list of statements will quickly introduce my question:
- An Interface called
IValidator
represents a contract for a validation process, - Some classes implement this interface with their own logic,
- The
Main
method owns a List ofIValidator
and uses them all to perform validation, - The
Main
method needs to log an error if a validation goes wrong, - I don't want classes implementing
IValidator
to know about log at all (to be able to concentrate all log actions inMain
), - I want the
Main
method to be able to log the reason of the validation failure.
The design question comes here: according to you what's the best way to have precise log about validation (开发者_如何学Ci.e. why did the validation fail) when classes implementing validation don't know about log ?
Here is some code illustrating my question:
public interface IValidator
{
bool Validate(String toValidate);
}
public class VowelValidator : IValidator
{
public bool Validate(string toValidate)
{
// .. Insert validation process here...
return true;
}
}
public class LenghtValidator : IValidator
{
public bool Validate(string toValidate)
{
// .. Insert validation process here...
return false;
}
}
public class Manager
{
public static void Main()
{
List<IValidator> validators = new List<IValidator>()
{
new VowelValidator(),
new LenghtValidator()
};
foreach (var validator in validators)
{
if (!validator.Validate("FooBar"))
{
/*
* Handle log here.
* I'd like to log something like "failed because string is too long"
* or "failed because string does not contain vowels".
*/
}
}
}
}
Some possibilities
- I could add a
String GetValidationDescription()
method to theIValidator
and use it inMain
to get a description of the validation that failed- But this means that every time I face that problem I'll have to add extra-methods only for log's sake. I'm not very happy with that.
- I could log the actual type of the
IValidator
that failed (i.e."Validation failed on LengthValidator"
) usingGetType()
. Then when I read the log I have to remember what that particular validator does and I can understand what went wrong.- This implies remembering / finding out what the validator does: no all information appears in the log.
Do you have other suggestions ? Are you aware of a common pattern for this kind of problem ?
Christophe.
Your current design for the validators is that they return true/false and do other information, but your requirement is to get some details about the failure, so you must change the interface or accept that the only thing you can log is that ValidtorXyz said "no".
Maintain a separation of concerns:
- The validator validates and reasonably report why validation fails
- The Logger logs information, so if it knew the the why it could log it, in some other scenario a UI might display the why
So logging is very clearly not the responsibility of the validator.
Adding a getValidationDescription() to the interface does not seem bad to me. It's quite legitimate for any object to describe itself - we do have toString() methods everywhere. There's nothing to say this is used specifically for logging.
However, a complex validator, for example a date validator, might fail for several reasons (eg. badly formatted input, month out of range ...) so I think you would do better to have as part of the result reasonForFailure.
One approach: create a return type object.
class ValidationResult{
boolean isValid;
String validationDescription;
String failureReason;
}
another approach provide a failure callback
validate(String input, IOnError callThisOnError);
where main provides:
class OnError implements IOnError {
void reportError(String input, String failureReason);
}
精彩评论