Reputation: 6091
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
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
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
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