設(shè)計(jì)模式系列:一文帶你領(lǐng)略“訪問者模式”的魅力
訪問者模式相對來說比較抽象和難以理解,可能單純地通過文字、類圖、案例代碼大家還是不太容易理解該模式,不過希望大家不要急躁,可以靜下心來用心的體會該設(shè)計(jì)模式的魅力。相信你一定會有所收獲。
基本介紹
訪問者模式的基本概念:封裝一些作用于某種數(shù)據(jù)結(jié)構(gòu)中元素上的操作。其有一個(gè)重要的特征是可以在不改變數(shù)據(jù)結(jié)構(gòu)的前提下定義一些新的操作。
簡單來說訪問者模式主要的作用就是將“數(shù)據(jù)結(jié)構(gòu)”和“數(shù)據(jù)操作”進(jìn)行分離,解決這兩者之間耦合性的問題。
訪問者模式的基本執(zhí)行原理就是在被訪問的類里添加一個(gè)接口,用于接待訪問者。
一般數(shù)情況下,當(dāng)我們需要對一個(gè)數(shù)據(jù)結(jié)構(gòu)中的元素進(jìn)行很多不同的操作,并且這些操作彼此之間并沒有關(guān)聯(lián),同時(shí)我們還想做到避免因?yàn)檫@些操作而“污染”了這些元素時(shí),就可考慮使用訪問者模式。
訪問者模式UML類圖

類圖講解
Visitor:抽象訪問角色;通常情況下該數(shù)據(jù)結(jié)構(gòu)中有幾個(gè)元素就會對應(yīng)的在該類中為每一個(gè)元素提供一個(gè)訪問操作(方法)。
ConcreteVisitor:具體訪問者角色;繼承了Visitor并實(shí)現(xiàn)了其中定義的所以方法。
Element:抽象元素角色;該類會定義一個(gè)accept(接收)方法,用于接收訪問者。
ConcreteElement:具體元素角色;繼承了Element并實(shí)現(xiàn)了其中定義的accept方法。
ObjectStruture:該類定義了數(shù)據(jù)結(jié)構(gòu)(對象結(jié)構(gòu)),管理了所有元素,并且可以枚舉它的元素(也就是遍歷)。
案例講解
案例:開發(fā)一個(gè)員工審批功能,具體為不同角色的員工可以進(jìn)行“同意”和“不同意”的審批。
抽象員工類 => 對應(yīng)Element(抽象元素角色)
- public abstract class Workers {
- // 提供一個(gè)讓訪問者訪問的方法
- public abstract void accept(Action action);
- }
具體員工類
- /**
- * 經(jīng)理
- */
- public class Manager extends Workers {
- /**
- * 這里用到了雙分派。
- * 第一次分派:在客戶端中將具體的Action作為參數(shù)傳遞到Manager中。
- * 第二次分派:Manager類調(diào)用Action中的具體方法,并將自己作為參數(shù)傳入。
- */
- @Override
- public void accept(Action action) {
- action.managerVerify(this);
- }
- }
- /**
- * 組長
- */
- public class GroupLeader extends Workers {
- @Override
- public void accept(Action action) {
- action.groupLeaderVerify(this);
- }
- }
抽象行為類 => 對應(yīng)Visitor(抽象訪問角色)
- public abstract class Action {
- // 經(jīng)理進(jìn)行審批
- public abstract void managerVerify(Manager manager);
- // 組長進(jìn)行審批
- public abstract void groupLeaderVerify(GroupLeader groupLeader);
- }
具體行為類
- /**
- * 同意
- */
- public class Agree extends Action {
- @Override
- public void managerVerify(Manager manager) {
- System.out.println("經(jīng)理的審核結(jié)果為同意!");
- }
- @Override
- public void groupLeaderVerify(GroupLeader groupLeader) {
- System.out.println("組長的審核結(jié)果為同意!");
- }
- }
- /**
- * 不同意
- */
- public class Disagree extends Action {
- @Override
- public void managerVerify(Manager manager) {
- System.out.println("經(jīng)理的審核結(jié)果為不同意!");
- }
- @Override
- public void groupLeaderVerify(GroupLeader groupLeader) {
- System.out.println("組長的審核結(jié)果為不同意!");
- }
- }
ObjectStructure類
- public class ObjectStructure {
- // 維護(hù)了一個(gè)集合
- private List<Workers> peoples = new ArrayList<>();
- // 增加
- public void attach(Workers workers) {
- peoples.add(workers);
- }
- // 移除
- public void detach(Workers workers) {
- peoples.remove(workers);
- }
- // 顯示測評情況
- public void display(Action action) {
- for (Workers people : peoples) {
- people.accept(action);
- }
- }
- }
客戶端測試類
- public class Client {
- public static void main(String[] args) {
- ObjectStructure objectStructure = new ObjectStructure();
- // 添加人
- objectStructure.attach(new Manager());
- objectStructure.attach(new GroupLeader());
- // 同意
- Agree agree = new Agree();
- objectStructure.display(agree);
- }
- }
執(zhí)行結(jié)果

總結(jié)
優(yōu)點(diǎn):
1、訪問者模式符合單一職責(zé)原則。
2、可以讓數(shù)據(jù)結(jié)構(gòu)和數(shù)據(jù)操作之間解耦。
3、避免了因?yàn)椴僮髟囟鴮ζ湓斐晌廴镜膯栴}。
4、讓程序具有擴(kuò)展性的情況下還大大增加了靈活性。
缺點(diǎn):
1、因?yàn)榫唧w的元素對訪問者公布了實(shí)現(xiàn)細(xì)節(jié),所以訪問者模式是違背了迪米特法則的。這樣做會導(dǎo)致元素變化比較困難。
2、因?yàn)樵L問者依賴的是具體的元素而不是其抽象父類,所有該模式還違背了依賴倒轉(zhuǎn)原則。
總結(jié) :訪問者模式適用于數(shù)據(jù)結(jié)構(gòu)相對穩(wěn)定并且功能需求還經(jīng)常變化的系統(tǒng)。