Существует ли шаблон проектирования для одного ввода, переданного n методам, каждый из которых возвращает входные данные для следующего метода - PullRequest
0 голосов
/ 23 января 2019

Я хотел бы знать, есть ли шаблон проектирования для этой проблемы:

Один вход используется для создания объекта (через конструктор или метод return я незабота), этот объект передается следующему методу или конструктору.Это повторяется на указанном пользователем наборе процессоров, очевидно, выбрасывая исключения, если есть разрыв в цепочке обязательных входов для процессоров.

Вывод всех, некоторых или ни одного из реализованных процессоров одинаковobject.

У меня запланировано около 6 процессоров, возможно, в будущем.

Композиция: Я не уверен, что мне нравится шаблон проектирования композиции, потому что не каждый объект предназначен для вывода этого процесса, и я не могу думать, как не выводить нулевые значениябез пользователя, знающего, что это будет нулевым.

Цепочка ответственности: Цепочка ответственности - это путь в соответствии с тем, что я слышал, однако я не уверен, что понимаю его.Предлагает ли этот шаблон проектирования передать n указателей на функцию, которая проходит через каждый из них?Если это так, я не уверен, как настроить функцию, которая получает n указателей на функции.

моя попытка:

У меня есть два интерфейса, которые унаследованы от n классов, т.е. (FirstProcessor, FirstInput, FirstOutput, SecondProcessor, SecondOutput, ThirdProcessor, ..,NProcessor, NOutput)

    IChainedOutput
    {
        IChainedOutput Input {get;} 
        FinalOutputOBj GetFinalOutput()
    }

    IChainedProcessor
    {
        IChainedOutput Run(IChainedOutput previousOutput)
    }

используется следующим образом:

    IChainedProcessor previous = FirstProcessor(originalInput)

    foreach(IChainedProcessor processor in processorList.Skip(1)
    {
        IChainedOutput current = processor.Run(previous)

        previous = current;       
    }

    FinalOutputObj output = previous.GetFinalOutput();

Проблемы:

  1. FinalOutputObj связан со всем процессоромреализации, что плохо.Он не состоит из всех членов дочернего класса IChainedOutput, но использует хорошее подмножество для вычисления других значений.
  2. FinalOutputObj составляется некорректно, и я не вижу, как мне избежать вывода нулевых значений, еслиСписок процессоров не содержит каждый реализованный процессор.
  3. В конструкторах дочерних классов много понижения, что является красным флагом для oop.Однако входы для каждого блока логики обработки совершенно разные.Первый вход - это пара векторов, второй вход - это выход первого, который включает несколько пользовательских типов и больше векторов и т. Д.
  4. Каждый IChainedOutput содержит ссылку на входы, использованные для его создания.в настоящее время существует однозначное отображение входа на процессор, но я не уверен в будущем.И это еще хуже, удручение.
  5. Мне бы не хотелось идеально организовывать список процессоров, так как другим разработчикам было бы слишком легко допускать ошибки здесь.поэтому следующий выбранный должен быть тот, который имеет правильный конструктор.

Ответы [ 3 ]

0 голосов
/ 23 января 2019

Каждый процессор преобразует определенный вход в определенный выход.Поэтому реализация процессора должна знать только два типа.

public interface IStepProcessor<TInput, TOutput>
{
    TOutput Process(TInput input);
}

В идеале клиентский код должен знать только два типа данных: входные данные и конечный продукт.Клиентскому коду не важно, были ли посреднические шаги.Клиент использует конвейер как черный ящик

public delegate TOutput Conveyor<TInput, TOutput>(TInput input);

И все же некоторый внешний код должен понимать, как выполняется все преобразование.Этот код должен знать все промежуточные типы данных и иметь доступ ко всем промежуточным процессорам.Лучше всего с внедрением зависимостей.

public class Factory
{
    private readonly IStepProcessor<IDataInput, IDataStep1> m_Step1;
    private readonly IStepProcessor<IDataStep1, IDataStep2> m_Task2;
    private readonly IStepProcessor<IDataStep2, IDataStep3> m_Task3;
    private readonly IStepProcessor<IDataStep3, IDataStepN> m_TaskN;
    private readonly IStepProcessor<IDataStepN, IDataOutput> m_FinalTask;

    public Factory(
        IStepProcessor<IDataInput, IDataStep1> task1,
        IStepProcessor<IDataStep1, IDataStep2> task2,
        IStepProcessor<IDataStep2, IDataStep3> task3,
        IStepProcessor<IDataStep3, IDataStepN> taskN,
        IStepProcessor<IDataStepN, IDataOutput> finalTask
        )
    {
        m_Step1 = task1;
        m_Task2 = task2;
        m_Task3 = task3;
        m_TaskN = taskN;
        m_FinalTask = finalTask;
    }
    public Conveyor<IDataInput, IDataOutput> BuildConveyor()
    {
        return (input) =>
        {
            return m_FinalTask.Process(
                m_TaskN.Process(
                    m_Task3.Process(
                        m_Task2.Process(
                            m_Step1.Process(input)))));
        };
    }
}

Вот мое предложение

public interface IDataInput { }
public interface IDataStep1 { }
public interface IDataStep2 { }
public interface IDataStep3 { }
public interface IDataStepN { }
public interface IDataOutput { }

public interface IStepProcessor<TInput, TOutput>
{
    TOutput Process(TInput input);
}

public delegate TOutput Conveyor<TInput, TOutput>(TInput input);

public class Factory
{
    private readonly IStepProcessor<IDataInput, IDataStep1> m_Step1;
    private readonly IStepProcessor<IDataStep1, IDataStep2> m_Task2;
    private readonly IStepProcessor<IDataStep2, IDataStep3> m_Task3;
    private readonly IStepProcessor<IDataStep3, IDataStepN> m_TaskN;
    private readonly IStepProcessor<IDataStepN, IDataOutput> m_FinalTask;

    public Factory(
        IStepProcessor<IDataInput, IDataStep1> task1,
        IStepProcessor<IDataStep1, IDataStep2> task2,
        IStepProcessor<IDataStep2, IDataStep3> task3,
        IStepProcessor<IDataStep3, IDataStepN> taskN,
        IStepProcessor<IDataStepN, IDataOutput> finalTask
        )
    {
        m_Step1 = task1;
        m_Task2 = task2;
        m_Task3 = task3;
        m_TaskN = taskN;
        m_FinalTask = finalTask;
    }
    public Conveyor<IDataInput, IDataOutput> BuildConveyor()
    {
        return (input) =>
        {
            return m_FinalTask.Process(
                m_TaskN.Process(
                    m_Task3.Process(
                        m_Task2.Process(
                            m_Step1.Process(input)))));
        };
    }
}


public class Client
{
    private readonly Conveyor<IDataInput, IDataOutput> m_Conveyor;

    public Client(Conveyor<IDataInput, IDataOutput> conveyor)
    {
        m_Conveyor = conveyor;
    }

    public void DealWithInputAfterTransformingIt(IDataInput input)
    {
        var output = m_Conveyor(input);
        Console.Write($"Mind your business here {typeof(IDataOutput).IsAssignableFrom(output.GetType())}");
    }
}

public class Program {

    public void StartingPoint(IServiceProvider serviceProvider)
    {
        ISomeDIContainer container = CreateDI();
        container.Register<IStepProcessor<IDataInput, IDataStep1>, Step1Imp>();
        container.Register<IStepProcessor<IDataStep1, IDataStep2>, Step2Imp>();
        container.Register<IStepProcessor<IDataStep2, IDataStep3>, Step3Imp>();
        container.Register<IStepProcessor<IDataStep3, IDataStepN>, StepNImp>();
        container.Register<IStepProcessor<IDataStepN, IDataOutput>, StepOImp>();

        container.Register<Factory>();

        Factory factory = container.Resolve<Factory>();
        var conveyor = factory.BuildConveyor();
        var client = new Client(conveyor);
    }
}
0 голосов
/ 01 февраля 2019

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

public interface IChainProcessor
{
    IChainOutput Run(IChainOutput previousOutput);
}

public interface IChainOutput
{
    string Value { get; }
}

public class OutputExample : IChainOutput
{
    public string Value { get; }

    public OutputExample(string value)
    {
        this.Value = value;
    }
}

public abstract class Processor : IChainProcessor
{
    protected IChainProcessor nextProcessor;

    public IChainOutput Run(IChainOutput previousOutput)
    {
        var myOutput = this.MyLogic(previousOutput);

        return this.nextProcessor == null ? myOutput : this.nextProcessor.Run(myOutput);
    }

    protected abstract IChainOutput MyLogic(IChainOutput input);
}

public class ProcessorA : Processor
{
    public ProcessorA() { }

    public ProcessorA(ProcessorB nextProcessor)
    {
        this.nextProcessor = nextProcessor;
    }

    protected override IChainOutput MyLogic(IChainOutput input)
    {
        return new OutputExample($"{input.Value} + Processor_A_Output"); 
    }
}

public class ProcessorB : ProcessorA
{
    public ProcessorB() { }

    public ProcessorB(ProcessorC nextProcessor)
    {
        this.nextProcessor = nextProcessor;
    }

    protected override IChainOutput MyLogic(IChainOutput input)
    {
        return new OutputExample($"{input.Value} + Processor_B_Output");  
    }
}

public class ProcessorC : ProcessorB
{
    protected override IChainOutput MyLogic(IChainOutput input)
    {
        return new OutputExample($"{input.Value} + Processor_C_Output"); 
    }
}

Использование будет примерно таким:

private static int Main(string[] args)
{
    var chain = new ProcessorA(new ProcessorB(new ProcessorC()));
    var simpleChain = new ProcessorA(new ProcessorC());
    var verySimpleChain = new ProcessorA();

    var initialInput = new OutputExample("Start");

    Console.WriteLine(chain.Run(initialInput).Value);
    Console.WriteLine(simpleChain.Run(initialInput).Value);
    Console.WriteLine(verySimpleChain.Run(initialInput).Value);

    return 0;
 }

Вывод этого примера:

Start + Processor_A_Output + Processor_B_Output + Processor_C_Output
Start + Processor_A_Output + Processor_C_Output
Start + Processor_A_Output

Абстрактный класс Processor предоставляет шаблонный метод, который вы можете реализовать в подклассах. Таким образом, каждый класс ProcessorX определяет только MyLogic(IChainOutput input)

Процессоры расширяют друг друга, чтобы обеспечить сохранение времени компиляции порядка процессора. Таким образом, невозможно построить цепочку, в которой ProcessorB предшествует ProcessorA. Тем не менее, возможно построить цепочку, в которой отсутствуют некоторые процессоры, как в приведенном выше примере.

Пример, который я привожу здесь, не учитывает окончательный результат, который, как я знаю, является одной из ваших основных задач. Для решения этой проблемы я бы предпочел создать класс отображения для преобразования IChainOutput в окончательный формат (я не знаю реальной структуры ваших данных, поэтому, возможно, это невозможно).

в некоторых моих случаях было бы целесообразно, чтобы выход одного процессора был входом для нескольких других процессоров

Используя этот шаблон, можно было бы также построить «дерево» процессора, а не цепочку, позволяя классу Processor иметь список следующих шагов. Ваше использование станет примерно таким:

var chain = new ProcessorA(new ProcessorB(new ProcessorC()), new ProcessorB(new ProcessorD()));

Надеюсь, это поможет вам.

0 голосов
/ 23 января 2019

Если я правильно понял ваше объяснение, вы можете использовать делегатов для решения вашей проблемы. Одним из важных моментов, касающихся делегатов, является то, что они могут быть объединены в цепочку, так что вы можете вызывать любое количество методов в одном событии.

...