Pattern: 訪問者模式
Class Diagram: 動物園管理員
這是一間動物園
1 2 3 4 5 6
| namespace DotNetCoreKata.DomainModels.ZooKeeper;
public interface IFeedable { void Feed(); }
|
1 2 3 4 5 6 7 8 9
| namespace DotNetCoreKata.DomainModels.ZooKeeper;
public class Monkey : IFeedable { public void Feed() { Console.WriteLine("Monkey eats banana"); } }
|
1 2 3 4 5 6 7 8 9
| namespace DotNetCoreKata.DomainModels.ZooKeeper;
public class Cow: IFeedable { public void Feed() { Console.WriteLine("Cow eats grass"); } }
|
1 2 3 4 5 6 7 8 9
| namespace DotNetCoreKata.DomainModels.ZooKeeper;
public class Lion: IFeedable { public void Feed() { Console.WriteLine("Lion eats meat"); } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| using DotNetCoreKata.DomainModels.ZooKeeper;
namespace DotNetCoreKata.Services.ZooKeeper;
public class Zoo { public void FeedAnimals() { List<IFeedable> animals = [ new Monkey(), new Cow(), new Lion() ];
foreach (var animal in animals) { animal.Feed(); } } }
|
目前是將要吃什麼的職責交由每個動物各自管理,
隨著功能變多,動物類別也會越來越肥大。
讓我們嘗試用訪問者模式改造它,
解耦餵食的邏輯。
首先是餵食者的邏輯 (Visitor)
1 2 3 4 5 6 7 8
| namespace DotNetCoreKata.DomainModels.ZooKeeper;
public interface IFeeder { void Feed(Monkey monkey); void Feed(Cow cow); void Feed(Lion lion); }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| namespace DotNetCoreKata.DomainModels.ZooKeeper;
public class Feeder: IFeeder { public void Feed(Monkey monkey) { Console.WriteLine("Monkey eats banana"); }
public void Feed(Cow cow) { Console.WriteLine("Cow eats grass"); }
public void Feed(Lion lion) { Console.WriteLine("Lion eats meat"); } }
|
接著改寫動物的邏輯 (Element)
1 2 3 4 5 6
| namespace DotNetCoreKata.DomainModels.ZooKeeper;
public interface IFeedable { void FeedBy(IFeeder feeder); }
|
1 2 3 4 5 6 7 8 9
| namespace DotNetCoreKata.DomainModels.ZooKeeper;
public class Monkey : IFeedable { public void FeedBy(IFeeder feeder) { feeder.Feed(this); } }
|
1 2 3 4 5 6 7 8 9
| namespace DotNetCoreKata.DomainModels.ZooKeeper;
public class Cow : IFeedable { public void FeedBy(IFeeder feeder) { feeder.Feed(this); } }
|
1 2 3 4 5 6 7 8 9
| namespace DotNetCoreKata.DomainModels.ZooKeeper;
public class Lion : IFeedable { public void FeedBy(IFeeder feeder) { feeder.Feed(this); } }
|
註:這邊也可以將三個動物的餵食邏輯放在一起,
不過要用到繼承跟調整餵食者的程式嗎。
最後改寫動物園的程式碼
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| using DotNetCoreKata.DomainModels.ZooKeeper;
namespace DotNetCoreKata.Services.ZooKeeper;
public class Zoo { public void FeedAnimals() { List<IFeedable> animals = [ new Monkey(), new Cow(), new Lion() ];
var feeder = new Feeder();
foreach (var animal in animals) { animal.FeedBy(feeder); } } }
|
現在我們將不同動物的餵食邏輯收斂在餵食者身上,
當新增新的動物時,可以修改餵食界面,
並實作相關邏輯。
當有新的訪問需求(比如看診),
我們也可以依樣畫葫蘆實作被看診界面與看診界面,
並實作看診者的邏輯。
ʕ •ᴥ•ʔ:Weija 於讀書會分享這個範例,
完全刷新了我之前用 PHP 寫範例的印象。
原來是在支援多型的語言,訪問者模式可以這麼漂亮。