Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Should I make the method virtual or abstract?

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?

like image 319
Jai Avatar asked Oct 19 '25 14:10

Jai


2 Answers

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.

like image 172
SBFrancies Avatar answered Oct 21 '25 04:10

SBFrancies


The short answer

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.


Your reasoning

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...


The deciding factor

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.

like image 22
Flater Avatar answered Oct 21 '25 05:10

Flater