Ejecutando la variación de la Cadena de Responsabilidad

Tengo una lista de tareas que básicamente es una variación del patrón de la cadena de responsabilidad.

Una tarea en mi pipeline se ve como abajo

internal interface IPTask
{
    bool CanExecute(T instance);
    T Process(T instance);
}

..y mi procesador se ve como

internal interface IProcessor
{
    T Execute(T instance);
}

y mi implementación concreta se ve así:

public class Processor : IProcessor
{
    private readonly ITasks tasks;

    public Processor(ITasks tasks)
    {
        this.tasks= tasks;
    }

    public T Execute(T instance)
    {
         var taskstoExecute = tasks.GetTasks()
                                   .Where(task => task.CanExecute(instance));

         taskstoExecute.ToList().ForEach(task=>task.Process(instance));

         return T;
    }
}

..y mis tareas se ven así:

internal interface ITasks
{
    IEnumerable> GetTasks();
}

T podrían ser instancias diferentes, pero vinculadas por un contrato genérico. Una de las tareas consiste en mapear el objeto entrante en un objeto completamente diferente y reenviar esa instancia desde allí en adelante.

Ahora, como verán, estoy ejecutando todas las tareas en proceso, me gustaría modificar esto a lo siguiente:

  • La entrada para el método Execute para la próxima tarea debe ser de la tarea ejecutada previamente.
  • Si el CanExecute falla para una tarea, la tubería debe detener el procesamiento de las tareas.

¿Puedes ayudarme a conseguir esto? ¿También esperaría que el código se estructurara de manera diferente para este propósito?

0

2 Respuestas

Qué tal esto:

public T Execute(T instance)
{
     T result = instance;
     foreach(var individual in tasks.GetTasks())
     {
         if(!individual.CanExecute()) break;

         result = individual.Process(result);
     }

     return result;
}

Como lo tienes actualmente, es mucho más como un patrón compuesto que una cadena de responsabilidad. Este cambio lo hace un poco más CoR-ish. Pero es más importante observar si satisface sus necesidades que utilizar la jerga de patrón de diseño correcto. :)

0
agregado

Con esta implementación, el CanProcess en realidad se usa para activar una excepción que detendrá el proceso.

Agregué una segunda implementación, llamada ExecuteWithPartial , que maneja la excepción, en caso de que ese sea el comportamiento que está esperando. Procesa, pero si hay un error, devuelve el resultado parcial hasta ese punto.

public class Processor : IProcessor
{
    //... rest of your code

    public T Execute(T instance)
    {
        return this._tasks.GetTasks().Aggregate(instance, (current, task) => InternalExecute(task, current));
    }

    public T ExecuteWithPartial(T instance)
    {
        var target = instance;
        try
        {
            foreach (var task in this._tasks.GetTasks())
            {
                target = InternalExecute(task, target);
            }
            return target;
        }
        catch (CantExecuteException)
        {
            return target;
        }
    }


    private static T InternalExecute(IPTask task, T instance)
    {
        if (!task.CanExecute(instance))
            throw new CantExecuteException();
        return task.Process(instance);
    }
}

Y la nueva clase de Excepción es:

public class CantExecuteException : Exception
{
}
0
agregado