Refactoring to Abstract Base Class – Default Implementation Strategies

abstract classcrefactoring

I have a class which aggregates some temporal data from a database and provides a bunch of methods to query said data. It looks like this:

public class InfoProvider
{
    public IEnumerable<Contract> GetContracts() => GetContracts(DateTime.Today);
    public IEnumerable<Contract> GetContracts(DateTime at) => GetContracts(at, at);
    public IEnumerable<Contract> GetContracts(DateTime from, DateTime to) { /* query contracts */ }

    public IEnumerable<Job> GetJobs() => GetJobs(DateTime.Today);
    public IEnumerable<Job> GetJobs(DateTime at) => GetJobs(at, at);
    public IEnumerable<Job> GetJobs(DateTime from, DateTime to) { /* query jobs */ }

    /* more methods like above ... */
}

Now I need to extend my application to support different representations of the data in the database. Those representations are identical for the most part but have some subtle differences. For example GetContracts in representation 1 should return contracts of type A, B or C while in representation 2 it should return contracts only of type A or B. On the contrary, GetJobs would be the same in both representations.

I want to do this extension by refactoring InfoProvider into an abstract class and derive concrete classes, InfoProviderRepresentation1 and InfoProviderRepresentation2 for each representation. But I waver where to put the existing code. I see those two options:

  1. Leave it in the original class
public abstract class InfoProvider
{
    public IEnumerable<Contract> GetContracts() => GetContracts(DateTime.Today);
    public IEnumerable<Contract> GetContracts(DateTime at) => GetContracts(at, at);
    public virtual IEnumerable<Contract> GetContracts(DateTime from, DateTime to) { /* query contracts */ }

    public IEnumerable<Job> GetJobs() => GetJobs(DateTime.Today);
    public IEnumerable<Job> GetJobs(DateTime at) => GetJobs(at, at);
    public virtual IEnumerable<Job> GetJobs(DateTime from, DateTime to) { /* query jobs */ }
}

public class InfoProviderRepresentation1 : InfoProvider
{
}

public class InfoProviderRepresentation2 : InfoProvider
{
    public override IEnumerable<Contract> GetContracts(DateTime from, DateTime to) { /* ... */ }
}
  1. Move the code to an other abstract class and derive my concrete classes from there:
public abstract class InfoProvider
{
    public IEnumerable<Contract> GetContracts() => GetContracts(DateTime.Today);
    public IEnumerable<Contract> GetContracts(DateTime at) => GetContracts(at, at);
    public abstract IEnumerable<Contract> GetContracts(DateTime from, DateTime to);

    public IEnumerable<Job> GetJobs() => GetJobs(DateTime.Today);
    public IEnumerable<Job> GetJobs(DateTime at) => GetJobs(at, at);
    public abstract IEnumerable<Job> GetJobs(DateTime from, DateTime to);
}

public abstract class DefaultInfoProvider
{
    public virtual IEnumerable<Contract> GetContracts(DateTime from, DateTime to) { /* query contracts */ }
    public virtual IEnumerable<Job> GetJobs(DateTime from, DateTime to) { /* query jobs */ }
}

public class InfoProviderRepresentation1 : DefaultInfoProvider
{
}

public class InfoProviderRepresentation2 : DefaultInfoProvider
{
    public override IEnumerable<Contract> GetContracts(DateTime from, DateTime to) { /* ... */ }
}

I find option 2 a bit more readable. On the other hand it needs more boilerplate code than option 1. Does option 2 have any more advantages over option 1?

Best Answer

This is the kind of question that you can ask a dozen different people and get a dozen different answers. I say do the simplest thing that solves your problem: if all you need to do is override GetContracts, don't even bother creating an abstract class, just derive from your existing class and override it. If you find yourself doing this a whole bunch and things start getting messy, then you can look into other strategies such as interface segregation and composition.