一般範例中的責任鏈,負責組合各實作的 Handler 時通常會如下方這樣組合,這樣的做法不管從操作上還是閱讀上都比較不友善:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 IHandler positiveEven = new PositiveEvenHandler(); IHandler negativeEven = new NegativeEvenHandler(); IHandler positiveOdd = new PositiveOddHandler(); IHandler negativeOdd = new NegativeOddHandler(); positiveEven.SetNext(negativeEven); negativeEven.SetNext(positiveOdd); positiveOdd.SetNext(negativeOdd); positiveEven.Handle(1 ); positiveEven.Handle(-1 ); positiveEven.Handle(2 ); positiveEven.Handle(-2 );
這篇主要是紀錄透過簡單的修改讓 Handler 能更簡單的組合責任鏈。
以 Fluent API 風格組合 根據上面的範例,如果能將組合責任鏈改成 Fluent API 風格,能有效提升易用性,如下:
1 2 3 4 5 6 7 8 9 10 11 IHandler root = new PositiveEvenHandler(); root.SetNext(new NegativeEvenHandler()) .SetNext(new PositiveOddHandler()) .SetNext(new NegativeOddHandler()); root.Handle(1 ); root.Handle(-1 ); root.Handle(2 ); root.Handle(-2 );
而各節點的實作重點則是將 void SetNext(IHandler next) 改成回傳下一個節點的實例 IHandler SetNext(IHandler next),非常簡單就能讓整個責任鏈更易用,如下範例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 public interface IHandler { IHandler SetNext (IHandler handler ) ; void Handle (int number ) ; } public class PositiveEvenHandler : IHandler { private IHandler _nextHandler; public IHandler SetNext (IHandler handler ) { _nextHandler = handler; return handler; } public void Handle (int number ) { if (number > 0 && number % 2 == 0 ) { Console.WriteLine($"Positive even number {number} " ); return ; } if (_nextHandler != null ) { _nextHandler.Handle(number); } } } public class NegativeEvenHandler : IHandler { private IHandler _nextHandler; public IHandler SetNext (IHandler handler ) { _nextHandler = handler; return handler; } public void Handle (int number ) { if (number < 0 && -number % 2 == 0 ) { Console.WriteLine($"Negative even number {number} " ); return ; } if (_nextHandler != null ) { _nextHandler.Handle(number); } } } public class PositiveOddHandler : IHandler { private IHandler _nextHandler; public IHandler SetNext (IHandler handler ) { _nextHandler = handler; return handler; } public void Handle (int number ) { if (number > 0 && number % 2 == 1 ) { Console.WriteLine($"Positive odd number {number} " ); return ; } if (_nextHandler != null ) { _nextHandler.Handle(number); } } } public class NegativeOddHandler : IHandler { private IHandler _nextHandler; public IHandler SetNext (IHandler handler ) { _nextHandler = handler; return handler; } public void Handle (int number ) { if (number < 0 && -number % 2 == 1 ) { Console.WriteLine($"Negative odd number {number} " ); return ; } if (_nextHandler != null ) { _nextHandler.Handle(number); } } }
其他作法 - Next 屬性取代 SetNext 方法 也是個簡化組合的程式碼的方式,但這種方式當整個責任鏈很長時可讀性稍差,搭配 DI 操作起來也不太順暢。 但優點就是直接用自動實作屬性就好,不用再額外實作 SetNext 方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 IHandler chain = new PositiveEvenHandler { Next = new { Next = new NegativeEvenHandler { Next = new PositiveOddHandler { Next = new NegativeOddHandler{} } } } };
結論 之前的文章提到過的,設計模式只是個樣板,實務上應該針對語言特性與應用場景做適當的調整。
參考 Design Pattern— 責任鏈模式(Chain of Responsibility Pattern)