I have an abstract class that does its own internal validation. It has another method that allows subclasses to do additional validation checks. Currently, I've made the method abstract
.
protected abstract bool ValidateFurther();
However, I'm seeing quite a number of subclasses being forced to override it just to return true
. I'm considering to make the method virtual
.
protected virtual bool ValidateFurther() => true;
Is it bad to assume that validation is going to be fine in the abstract class? I'm worried that subclasses may not notice it and ended up not overriding it even when it is needed. Which is the more suitable approach here?
You could add another layer into your design.
public abstract class Base
{
protected abstract bool ValidateFurther();
}
public abstract class BaseWithValidation : Base
{
protected override bool ValidateFurther() => true;
}
If a significant subset of your inherited classes should just return true you can use BaseWithValidation
to avoid having to repeat the code everywhere; for anything else use Base
.
If this class' (and all of its derived class') purpose does not always necessitate validation, then you should go with virtual
, otherwise abstract
.
In other words, is validation a cornerstone of this class' purpose? (yes = abstract, no = virtual)
I suspect that virtual
is the better approach here, but not for the reason you're thinking it is. The rest of this answer elaborates on why your reasoning isn't the deciding factor here, and what actually is the deciding factor.
I'm seeing quite a number of subclasses being forced to override it just to return true.
I suspect you're succumbing to the programmer's reflex: "I see this repeated and must write code to avoid this repetition!"
While that is generally a good approach, it can also be misapplied when you start applying this to things that happen to be the same rather than expressing the same functional purpose.
The example I tend to use to address that point is the following:
public class Book
{
public string Title { get; set; }
public DateTime CreatedOn { get; set; }
}
public class EmployeeJob
{
public string Title { get; set; }
public DateTime CreatedOn { get; set; }
}
There is definitely value to abstracting the CreatedOn
property, as these entities are both audited data entities. The CreatedOn
property is part of that audited entity, and its existence in both Book
and EmployeeJob
stems from these classes both being audited entities.
If a change is made to audited entities (e.g. they no longer track creation date), then that change needs to automatically persist to all audited entities. When you use shared logic, that automatically happens.
But does Title
need to be abstracted into a shared logic? No. There is no functional overlap here. Yes, these properties have the same name and type, but they share no common logic whatsoever. They just happen to be equal to each other right now, but they are not tied to one another.
If a change is made to one Title
property (e.g. it now becomes a Guid
FK to a table of job titles), that change doesn't automatically reflect on the other (e.g. a book title would still just be a string). Implementing these Title
properties using shared logic would actually cause a problem down the line instead of solve one.
In short: sometimes programmers seek more patterns than they need. Or if you allow me to quote Jurassic Park...
I'm considering to make the method virtual.
Whether you make it abstract or virtual depends on one specific considerations (not DRY, as addressed above): Do you wish to provide a default implementation, or would you prefer to enforce that every consumer (i.e. derived class) evaluate the implementation of this method for themselves?
Neither of these are objectively better than the other, it's a matter of which fits best for you current scenario.
I'm seeing quite a number of subclasses being forced to override it just to return true.
I infer from this that you're essentially skipping validation in these classes, so in this case I would opt for the virtual
approach since this class' (and all of its derived class') purpose does not always necessitate validation (again, that is my interpretation based on your explanation).
In other words, is validation a cornerstone of this class' purpose? (yes = abstract, no = virtual). You didn't specify your class or its purpose so I can't make that final call.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With