Jakob Busk Sørensen
Jakob Busk Sørensen

Reputation: 6091

Abstract method, which may be empty

I have an abstract class, which contains an abstract method, which is only required sometimes. A bit simply put, the reason for this is that the class executes some code, which only sometimes results in output (which then needs to be handled). So the implementations of the abstract class, which does get output, needs to implement this method, whereas the implementations with no output actually could do without. The abstract class looks something like this:

abstract class AbstractWorker
{
    public virtual Execute()
    {
        OutputModel output = await PerformActions();
        await HandleOutput(output);
    }        

    protected abstract Task<OutputModel> PerformActions();
    protected abstract Task HandleOutput(OutputModel);
}

I cannot implement the the methods PerformActions() and HandleOutput() as they are very individual to the specific implementation of AbstractWorker. And, as I said, there is not always an output to handle, but I need to force the method, in case it does have output. So the implementation wood look something like this:

public class ConcreteWorker : AbstractWorker
{
    protected override async Task<OutputModel> PerformActions() 
    {
        // ...do stuff here, which produces no output
        return null;
    }

    protected override async Task HandleOutput(OutputModel output) 
    {
        // Do nothing, since there is no output
        return;
    }
}

While the above does work, it seems somewhat silly. But it is required, for the cases where output is generated. Is there some sort of smarter way to do it?

Upvotes: 0

Views: 1936

Answers (3)

Zohar Peled
Zohar Peled

Reputation: 82474

The object oriented way to solve this is to add another abstraction layer.

Have the AbstractWorker class implement an interface (IAbstractWorker) that has only the Execute() method. (well, since it's an async method let's have it return a Task and call it ExecuteAsync to follow best practice)

Have workers that doesn't need to handle the output implement that interface directly,
and workers that need to handle the output use the current abstract class.

Basically something like this:

interface IAbstractWorker
{
    Task ExecuteAsync();
}

abstract class AbstractSepcializedWorker : IAbstractWorker
{
    public async Task ExecuteAsync()
    {
        OutputModel output = await PerformActions();
        await HandleOutput(output);
    }        

    protected abstract Task<OutputModel> PerformActionsAsync();
    protected abstract Task HandleOutputAsync(OutputModel);
}


class Worker : IAbstractWorker
{
    public async Task ExecuteAsync()
    {
        // implementation
    }
}

class SepcializedWorker : AbstractSepcializedWorker
{

    protected override Task<OutputModel> PerformActionsAsync()
    {
        // implementation
    }
    protected override Task HandleOutputAsync(OutputModel)
    {
        // implementation
    }
}

Then you have all your worker classes implement the IAbstractWorker interface, and you can easily create new concrete worker classes that either implement the interface directly or inherit the abstract class (and therefor implement it via inheritance).

Upvotes: 1

MistyK
MistyK

Reputation: 6222

You can make them virtual with default logic in the base class.

protected virtual async Task<OutputModel> PerformActions(){
    // ...do stuff here, which produces no output
   return null;
}

protected virtual async Task HandleOutput(OutputModel output) 
{ 
    // Do nothing, since there is no output
    return;
}

Upvotes: 2

vsarunov
vsarunov

Reputation: 1547

You should not be implementing methods that you are not using. SOLID principles, Interface segregation:

https://en.wikipedia.org/wiki/Interface_segregation_principle

I would put another abstraction layer in between the classes that use the methods and those that do not use it.

Upvotes: 4

Related Questions