Visitor 模式可以再不改變現有類別結構的情況下,像類別結構增加新方法。另一個可以達到相同目的的模式是 Decorator 模式。
想像假設要實作一個方法,乘客要下車時需要按下車鈴通知司機。如果將該方法宣告在介面中,並讓各個類別實作,這樣也不是不行,但是如果之後有更多需求,就會變得需要頻繁異動該類別。
所以可以這樣想:讓有該需求的使用者去呼叫實作該需求的類別即可,即我實作一個「下車按鈴」的類別,讓公車類別來使用,如果交通工具是機車的話就不會使用到該類別。
按鈴下公車 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 using System;namespace DemoCode.DesignPattern.Visitor.Decorator { public interface ITransportation { void TakeASeat ( ) ; void TakeOff ( ) ; } }
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 using System;namespace DemoCode.DesignPattern.Visitor.Decorator { public class Bus : ITransportation { public void TakeASeat ( ) { Console.WriteLine("找個座位" ); } public void TakeOff ( ) { Console.WriteLine("司機先生我要下車" ); } } }
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 using System;namespace DemoCode.DesignPattern.Visitor.Decorator { public class RingBeforeTakeOffBus : ITransportation { private ITransportation itsTransportation; public RingBeforeTakeOffBus (ITransportation transportation ) { this .itsTransportation = transportation; } public void TakeASeat ( ) { itsTransportation.TakeASeat(); } public void TakeOff ( ) { Console.WriteLine("按鈴下車" ); itsTransportation.TakeOff(); } } }
收攏重複的程式碼 如果現在要實作其他的裝飾者類別,例如下車前要刷卡 (BeepBeforeTakeOff),但是如果照前面的寫法,TakeASeat() 這個方法的程式碼會重複出現於 BipBeforeTakeOff 及 RingBeforeTakeOff 類別,我們可以把會重複的程式碼收攏在一個類別即可。
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 using System;namespace DemoCode.DesignPattern.Visitor.Decorator { public class BusDecorator : ITransportation { private ITransportation itsTransportation; public BusDecorator (ITransportation transportation ) { this .itsTransportation = transportation; } public void TakeASeat ( ) { itsTransportation.TakeASeat(); } public void TakeOff ( ) { itsTransportation.TakeOff(); } } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 using System;namespace DemoCode.DesignPattern.Visitor.Decorator { public class BeepBeforeTakeOffBus : BusDecorator { private ITransportation itsTransportation; public BeepBeforeTakeOffBus (ITransportation transportation ) : base (transportation ) { itsTransportation = transportation; } public new void TakeOff ( ) { Console.WriteLine("下車前刷卡" ); itsTransportation.TakeOff(); } } }
輸出結果:
1 2 3 4 5 6 7 8 9 10 class Program { static void Main (string [] args ) { var bus = new DemoCode.DesignPattern.Visitor.Decorator.Bus(); var decorator = new DemoCode.DesignPattern.Visitor.Decorator.BeepBeforeTakeOffBus(bus); decorator.TakeOff(); } }
完整程式碼請參考
結論 裝飾者模式提供了另一種方法,來達到在不改變既有的類別下增加新的方法。裝飾者模式與其他模式的比較可以 參考此篇
參考 VISITOR 模式.無瑕的程式碼 敏捷完整篇:物件導向原則、設計模式與 C# 實踐 iThome 鐵人競賽 - [Design Pattern] Decorator 裝飾者模式