Ausführung der Variationskette der Verantwortung

Ich habe eine Pipeline von Aufgaben, die im Grunde genommen eine Variation der Verantwortungskette ist.

Eine Aufgabe in meiner Pipeline sieht wie folgt aus

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

..und mein Prozessor sieht so aus

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

und meine konkrete Implementierung sieht so aus:

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

..und meine Aufgaben sehen so aus:

internal interface ITasks
{
    IEnumerable> GetTasks();
}

T könnte verschiedene Instanzen sein, aber durch einen generischen Vertrag gebunden sein. Eine der Aufgaben besteht darin, das ankommende Objekt in ein völlig anderes Objekt abzubilden und diese Instanz von dort weiterzuleiten.

Jetzt, wie Sie sehen würden, führe ich alle Aufgaben in der Pipeline aus. Ich möchte dies wie folgt ändern:

  • Die Eingabe für die Methode Execute für die nächste Aufgabe sollte von der zuvor ausgeführten Aufgabe stammen.
  • Wenn der CanExecute für eine Aufgabe fehlschlägt, sollte die Pipeline die Verarbeitung der Aufgaben beenden.

Kannst du mir bitte dabei helfen? Würden Sie auch erwarten, dass der Code für diesen Zweck anders strukturiert ist?

0
hinzugefügt bearbeitet
Ansichten: 1

2 Antworten

Wie wäre es damit:

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

         result = individual.Process(result);
     }

     return result;
}

Wie Sie es derzeit haben, ist es eher ein zusammengesetztes Muster als eine Kette von Verantwortung. Diese Änderung macht es ein wenig mehr CoR-ish. Es ist jedoch wichtiger zu beachten, ob es Ihren Anforderungen entspricht, als den richtigen Designmuster-Jargon zu verwenden. :)

0
hinzugefügt

Bei dieser Implementierung wird der CanProcess tatsächlich verwendet, um eine Ausnahme auszulösen, die den Prozess stoppt.

Ich habe eine zweite Implementierung mit dem Namen ExecuteWithPartial hinzugefügt, die die Ausnahme behandelt, falls dies das von Ihnen erwartete Verhalten ist. Es verarbeitet, aber wenn es einen Fehler gibt, gibt es das Teilergebnis bis zu diesem Punkt zurück.

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

Und die neue Exception-Klasse ist:

public class CantExecuteException : Exception
{
}
0
hinzugefügt