【設計模式】通過一個簡單的案例理解-訪問者模式(Visitor Pattern)
theme: condensed-night-purple
攜手創作,共同成長!這是我參與「掘金日新計劃 · 8 月更文挑戰」的第3天,點選檢視活動詳情
場景切入
動物園中有多個場館,比如豹子館,海豚館,大象館等等,有些場館是需要特殊收費的,動物園針對不同型別的遊客有不同的收費方式,比如學生半價。
這個場景下,包括以下要素:動物園
,動物園中的各個場館
,不同型別的遊客
,不同型別的遊客票價不同
。
動物園就相當於一個物件結構
,該結構包含具體的元素
-各個場館,每個場館(元素)都有接待遊客
(visitor)的方法(accept)。
這些被處理的 資料元素相對穩定 (動物園中的場館一般比較穩定)而 訪問方式多種多樣 (比如學生散客,學生團體,普通遊客,團體遊客等不同的訪問方式)的資料結構,如果用 訪問者模式 來處理比較方便。
訪問者模式能 把處理方法從資料結構中分離出來 ,並可以根據需要增加新的處理方法,且不用修改原來的程式程式碼與資料結構,這提高了程式的擴充套件性和靈活性。
訪問者模式的結構
通過上面場景的分析,訪問者( Visitor
)模式實現的關鍵是 如何將作用於元素的操作分離出來封裝成獨立的類 ,其基本結構如下:
- 抽象的訪問者(Visitor):訪問具體元素的介面,為每個具體元素類對應一個訪問操作
visitXX()
,其引數為某個具體的元素。 - 具體的訪問者(ConcreteVisitor):實現抽象訪問者角色中宣告的各個訪問操作,確定訪問者訪問一個元素時該做什麼。
- 抽象元素(Element):宣告一個包含接受操作
accept()
的介面,其引數為訪問者物件(遊客
)。 - 具體元素(ConcreteElement):實現抽象元素角色提供的
accept()
操作,其方法體通常都是visitor.visitXX(this)
,另外具體元素中可能還包含本身業務邏輯的相關操作。 - 物件結構(Object Structure):一個包含元素角色的容器,提供讓訪問者物件遍歷容器中的所有元素的方法,通常由
List
、Set
、Map
等聚合類實現。本例中的動物園
就可抽象成一個物件結構。
針對設定的動物園場景:
動物園
由多個場館
組成,是聚合關係;場館介面
由具體的獵豹館
、海豚館
等等具體的場館實現;訪問者
訪問場館
,且其由不同的訪問者進行實現;
根據以上資訊,用訪問者模式實現的類圖為:
程式碼實現
前面已經分析出需要抽象出來的類了,我們把它們轉化成程式碼。
物件結構(Object Structure):
```java
//物件結構角色:動物園
class Zoo {
//場館集合
private List
//接待遊客
public void accept(Visitor visitor) {
for (ScenerySpot scenerySpot : list) {
scenerySpot.accept(visitor);
}
}
public void add(ScenerySpot scenerySpot) {
list.add(scenerySpot);
}
public void remove(ScenerySpot scenerySpot) {
list.remove(scenerySpot);
}
} ```
抽象元素和具體元素:
```java //抽象元素:場館景點 interface ScenerySpot { //接待訪問者 void accept(Visitor visitor); //票價(單位是分) Integer ticketRate(); }
//具體元素:豹子館 class LeopardSpot implements ScenerySpot { @Override public void accept(Visitor visitor) { visitor.visitLeopardSpot(this); }
@Override
public Integer ticketRate() {
//票價15元
return 1500;
}
}
//具體元素:海豚館 class DolphinSpot implements ScenerySpot { @Override public void accept(Visitor visitor) { visitor.visitDolphinSpot(this); }
@Override
public Integer ticketRate() {
//票價20元
return 2000;
}
} ```
抽象訪問者和具體訪問者:
```java //抽象訪問者:遊客 interface Visitor { //參觀獵豹館 void visitLeopardSpot(LeopardSpot leopardSpot); //參觀海豚館 void visitDolphinSpot(DolphinSpot dolphinSpot); }
//具體的訪問者:學生遊客 class StudentVisitor implements Visitor {
@Override
public void visitLeopardSpot(LeopardSpot leopardSpot) {
//學生票打五折
int v = (int) (leopardSpot.ticketRate() * 0.5);
System.out.println("學生遊客遊覽豹子館票價:" + v);
}
@Override
public void visitDolphinSpot(DolphinSpot dolphinSpot) {
//學生票打五折
int v = (int) (dolphinSpot.ticketRate() * 0.5);
System.out.println("學生遊客遊覽海豚館票價:" + v);
}
}
//具體的訪問者:普通遊客 class CommonVisitor implements Visitor {
@Override
public void visitLeopardSpot(LeopardSpot leopardSpot) {
System.out.println("普通遊客遊覽豹子館票價:" + leopardSpot.ticketRate());
}
@Override
public void visitDolphinSpot(DolphinSpot dolphinSpot) {
System.out.println("普通遊客遊覽海豚館票價:" + dolphinSpot.ticketRate());
}
} ```
使用:
```java public class VisitorPattern { public static void main(String[] args) { Zoo zoo = new Zoo(); //新增遊覽的場館 zoo.add(new LeopardSpot()); zoo.add(new DolphinSpot()); //還可以新增其他場館
//動物園接待不同型別的遊客
//學生遊客
zoo.accept(new StudentVisitor());
System.out.println("==========================");
//普通遊客
zoo.accept(new CommonVisitor());
//還可以定義其他型別的遊客,比如公司團體遊客等
}
} ```
執行結果:
學生遊客遊覽豹子館票價:750
學生遊客遊覽海豚館票價:1000
==========================
普通遊客遊覽豹子館票價:1500
普通遊客遊覽海豚館票價:2000
從程式碼也可以看出來,我們不需要再動 動物園 Zoo
這個結構內部的內容了,需要建造場館實現 ScenerySpot
,或者接待其他型別的遊客實現 Visitor
即可。
應用場景
通常在以下情況可以考慮使用訪問者(Visitor)模式: - 物件結構相對穩定,但其操作演算法經常變化的程式。 - 物件結構中的物件需要提供多種不同且不相關的操作,而且要避免讓這些操作的變化影響物件的結構。 - 物件結構包含很多型別的物件,希望對這些物件實施一些依賴於其具體型別的操作。
- Java最強大的技術之一:反射
- 【設計模式】通過一個簡單的案例理解-訪問者模式(Visitor Pattern)
- 【設計模式】模板模式,學會它咱也寫出優雅健壯的程式碼!
- 【設計模式】通俗易懂版責任鏈模式
- 【設計模式】代理模式那些事兒:靜態代理,動態代理,JDK的動態代理,cglib,Spring AOP
- 萬字長文 | Spring Cloud Alibaba元件之Nacos實戰及Nacos客戶端服務註冊原始碼解析
- 【設計模式】只需體驗三分鐘,你就會跟我一樣瞭解Facade和Mediator模式
- 【設計模式】工廠系列-FactoryMethod,AbstractFactory,Spring IOC
- 【資料結構】| 連結串列資料結構及其簡單玩法解析
- 系統架構演進與Spring Cloud Alibaba簡介
- 【FastDFS】一文學會一個分散式檔案系統!
- 【MySQL 8】MySQL 5.7都即將停只維護了,是時候學習一波MySQL 8了!
- 使用Rancher部署管理K8S叢集,真香!
- 玩轉Docker映象倉庫-Docker Registry及Harbor
- 【設計模式】各個擊破單例模式的8種寫法
- 容器化技術之Docker-從入地到上天
- 【RocketMQ】RocketMQ叢集,RocketMQ-on-DLedger可容災叢集
- 【RocketMQ】基於RocketMQ的分散式事務
- 【RocketMQ】近距離感受RocketMQ如何收發訊息,有備而來!
- 【RocketMQ】RocketMQ入門之閃電三連鞭:訊息佇列、RocketMQ介紹及安裝使用