2012-06-21 15 views
5

Mam szereg zadań, które zasadniczo są odmianą schematu odpowiedzialności.Wykonywanie łańcucha odpowiedzialności Zmiana

Zadanie w moim rurociągu wygląda jak poniżej

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

..i mój procesor wygląda

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

i moja konkretna realizacja wygląda następująco:

public class Processor<T> : IProcessor<T> 
{ 
    private readonly ITasks<T> tasks; 

    public Processor(ITasks<T> 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; 
    } 
} 

.. a moje zadania wyglądają tak:

internal interface ITasks<T> 
{ 
    IEnumerable<IPTask<T>> GetTasks(); 
} 

T może być różną instancją, ale związaną umową ogólną. Jednym z zadań jest mapowanie przychodzącego obiektu na zupełnie inny obiekt i dalsze przekazywanie tej instancji.

Teraz, jak można zobaczyć, ja wykonując wszystkie zadania w rurociągu, chciałbym zmodyfikować to do następującej:

  • wejściowe dla metody Execute do następnego zadania powinny być z tego wcześniej wykonane zadanie.
  • Jeśli zadanie nie powiedzie się CanExecute, rurociąg powinien przerwać przetwarzanie zadań.

Czy możesz mi pomóc w uzyskaniu tego. Czy spodziewałbyś się, że kod będzie inaczej skonstruowany w tym celu?

Odpowiedz

3

Jak o tym:

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

     result = individual.Process(result); 
    } 

    return result; 
} 

Jak masz go obecnie, jest o wiele bardziej złożonej strukturze niż łańcuch odpowiedzialność. Ta zmiana sprawia, że ​​jest trochę więcej KR-u. Ale ważniejsze jest, aby zwrócić uwagę, czy spełnia on twoje potrzeby, niż używać właściwego żargonu wzoru projektu. :)

3

Dzięki tej implementacji CanProcess jest rzeczywiście używany do wyzwalania wyjątku, który zatrzyma proces.

Dodałem drugą implementację o nazwie ExecuteWithPartial, która obsługuje wyjątek, na wypadek, gdyby takie zachowanie się spodziewano. Przetwarza, ale jeśli wystąpi błąd, zwraca częściowy wynik do tego momentu.

public class Processor<T> : IProcessor<T> 
{ 
    //... 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<T> task, T instance) 
    { 
     if (!task.CanExecute(instance)) 
      throw new CantExecuteException(); 
     return task.Process(instance); 
    } 
} 

A nowa klasa Wyjątek jest:

public class CantExecuteException : Exception 
{ 
}