旧访客设计模式的新生活
介紹
訪客 [1、2]是眾所周知的經典設計模式。 有很多資源對其進行了詳細說明。 在不深入研究實現的情況下,我將簡要提醒一下該模式的概念,解釋其優點和缺點,并提出一些可以使用Java編程語言輕松應用于其的改進。
古典游客
[Visitor] 允許在運行時將一個或多個操作應用于一組對象,從而將操作與對象結構分離。
(四人幫)
該模式基于通常稱為的接口。 Visitable具有由模型類和一組來實現Visitors實現為每個相關的模型類方法(算法)。
public interface Visitable { public void accept(Visitor visitor); } public class Book implements Visitable { ....... @Override public void accept(Visitor visitor) {visitor.visit( this )}; ....... } public class Cd implements Visitable { ....... @Override public void accept(Visitor visitor) {visitor.visit( this )}; ....... } Visitor { interface Visitor { public void visit(Book book); public void visit(Magazine magazine); public void visit(Cd cd); }現在我們可以實現各種visitors ,例如
- PrintVisitor是提供打印Visitable
- DbVisitor其存儲在數據庫中的DbVisitor ,
- 將其添加到購物車的ShoppingCart
等等
訪客模式的缺點
經典訪客模式的改進
返回值
首先,讓我們將返回值添加到Visitor接口。 通用定義可以使用泛型來完成。
public interface Visitable { public <R> R accept(Visitor<R> visitor); } interface Visitor<R> { public R visit(Book book); public R visit(Magazine magazine); public R visit(Cd cd); }好吧,這很容易。 現在,我們可以將任何能帶來價值的Visitor應用于我們的圖書。 例如, DbVisitor可能返回DB(整數)中已更改記錄的數量,而ToJson訪問者可能會將我們對象的JSON表示形式返回為String。 (該示例可能不太有機,在現實生活中,我們通常使用其他技術將對象序列化為JSON,但是就其在理論上可以使用Visitor模式而言,已經足夠了)。
默認實現
接下來,讓我們感謝Java 8在接口內保留默認實現的能力:
public interface Visitable<R> { default R accept(Visitor<R> visitor) { return visitor.visit( this ); } }現在,實現Visitable類本身不必實現>visit() :在大多數情況下,默認實現就足夠了。
上面建議的改進解決了缺點#1和#2。
單訪客
讓我們嘗試應用進一步的改進。 首先,讓我們定義接口MonoVisitor如下:
public interface MonoVisitor<T, R> { R visit(T t); }Visitor名稱已更改為MonoVisitor以避免名稱沖突和可能的混淆。 通過這本書, visitor定義了許多重載方法visit() 。 他們每個人都為每個Visitable接受不同類型的參數。 因此,根據定義, Visitor不能是通用的。 必須在項目級別上定義和維護它。 MonoVisitor僅定義一種方法。 類型安全由泛型保證。 即使使用不同的通用參數,單個類也無法多次實現相同的接口。 這意味著即使將MonoVisitor多個實現分為一組,我們也必須保留它們。
功能參考,而不是訪客
由于MonoVisitor只有一種業務方法,因此我們必須為每個模型類創建實現。 但是,我們不想創建單獨的頂級類,而是希望將它們分組為一個類。 這個新的visitor在各種Visitable類與java.util.Function實現之間持有Map,并將visit()方法的調用分派給特定的實現。
因此,讓我們看一下MapVisitor。
public class MapVisitor<R> implements Function<Class<? extends Visitable>, MonoVisitor<? extends Visitable, R>> { private final Map<Class<? extends Visitable>, MonoVisitor<? extends Visitable, R>> visitors; MapVisitor(Map<Class<? extends Visitable>, MonoVisitor<? extends Visitable, R>> visitors) { this .visitors = visitors; } @Override public MonoVisitor apply(Class clazz) { return visitors.get(clazz); } }MapVisitor
- 實現Function
為了檢索特定的實現(為便于閱讀,此處省略了完整的泛型;有關詳細定義,請查看代碼段)
- 接收映射中類和實現之間的映射
- 檢索適合給定類的特定實現
MapVisitor具有程序包專用的構造函數。 使用特殊構建器完成的MapVisitor初始化非常簡單且靈活:
MapVisitor<Void> printVisitor = MapVisitor.builder(Void. class ) .with(Book. class , book -> {System.out.println(book.getTitle()); return null ;}) .with(Magazine. class , magazine -> {System.out.println(magazine.getName()); return null ;}) .build();MapVisitor的用法類似于傳統的Visitor :
someBook.accept(printVisitor); someMagazine.accept(printVisitor);我們的MapVisitor還有一個好處。 必須實現在傳統訪問者的接口中聲明的所有方法。 但是,通常無法實現某些方法。
例如,我們想要實現演示動物可以執行的各種動作的應用程序。 用戶可以選擇一種動物,然后通過從菜單中選擇特定的動作來使它做某事。
這是動物名單: Duck, Penguin, Wale, Ostrich 這是動作列表: Walk, Fly, Swim. 我們決定按操作設置訪問者: WalkVisitor, FlyVisitor, SwimVisitor 。 鴨子可以做所有三個動作,企鵝不能飛,瓦爾只能游泳, 鴕鳥只能行走。 因此,如果用戶試圖使Wale走路或Ostrich飛行,我們決定拋出異常。 但是這種行為不是用戶友好的。 確實,用戶只有在按下操作按鈕時才會收到錯誤消息。 我們可能更希望禁用不相關的按鈕。 MapVisitor允許此操作而無需其他數據結構或代碼重復。 我們甚至不必定義new或擴展任何其他接口。 相反,我們更喜歡使用標準接口java.util.Predicate :
現在我們可以調用函數test()來定義是否必須啟用或顯示選定動物的操作按鈕。
github上提供了此處使用的示例的完整源代碼。
結論
本文演示了一些改進,這些改進使舊的Visitor模式變得更加靈活和強大。 建議的實現方式避免了實現經典的Vistor模式所需的某些樣板代碼。 以下是上述改進的簡要清單。
參考文獻
翻譯自: https://www.javacodegeeks.com/2019/03/new-life-old-visitor-design-pattern.html
總結
以上是生活随笔為你收集整理的旧访客设计模式的新生活的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: wcl种族天赋改动(wlk种族天赋分析)
- 下一篇: 为什么电脑打不开新浪微博(电脑新浪微博打