Абстрактный метод, который может быть пустым - PullRequest
0 голосов
/ 20 мая 2019

У меня есть абстрактный класс, который содержит абстрактный метод, который требуется только иногда. Проще говоря, причина этого в том, что класс выполняет некоторый код, который только иногда приводит к выводу (который затем должен быть обработан). Таким образом, реализации абстрактного класса, который получает выходные данные, должны реализовать этот метод, в то время как реализации без вывода на самом деле могут обойтись. Абстрактный класс выглядит примерно так:

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

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

Я не могу реализовать методы PerformActions() и HandleOutput(), поскольку они очень индивидуальны для конкретной реализации AbstractWorker. И, как я уже сказал, не всегда есть выход для обработки, но мне нужно принудительно вызвать метод, если он действительно имеет выход. Так что реализация дерева выглядит примерно так:

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;
    }
}

Хотя вышесказанное работает, это выглядит несколько глупо. Но это требуется для случаев, когда генерируется выходной сигнал. Есть ли какой-нибудь умный способ сделать это?

Ответы [ 3 ]

4 голосов
/ 20 мая 2019

Вы не должны реализовывать методы, которые вы не используете. Твердые принципы, разделение интерфейса:

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

Я бы поставил еще один уровень абстракции между классами, которые используют методы, и теми, которые его не используют.

2 голосов
/ 20 мая 2019

Вы можете сделать их виртуальными с помощью логики по умолчанию в базовом классе.

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;
}
0 голосов
/ 28 мая 2019

Объектно-ориентированный способ решить эту проблему - добавить еще один уровень абстракции.

Пусть класс AbstractWorker реализует интерфейс (IAbstractWorker), который имеет только метод Execute(). (хорошо, поскольку это асинхронный метод, давайте вернем ему Task и назовем его ExecuteAsync, чтобы следовать рекомендациям)

Есть работники, которым не нужно обрабатывать выход, реализующий этот интерфейс напрямую,
и работники, которым необходимо обработать вывод, используют текущий абстрактный класс.

В основном что-то вроде этого:

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

Затем у вас есть все ваши рабочие классы, реализующие интерфейс IAbstractWorker, и вы можете легко создавать новые конкретные рабочие классы, которые либо реализуют интерфейс напрямую, либо наследуют абстрактный класс (и, следовательно, реализуют его через наследование).

...