深入理解Java設(shè)計(jì)模式之訪問者模式
一、什么是訪問者模式
定義:表示一個(gè)作用于其對(duì)象結(jié)構(gòu)中的各元素的操作,它使你可以在不改變各元素類的前提下定義作用于這些元素的新操作。
可以對(duì)定義這么理解:有這么一個(gè)操作,它是作用于一些元素之上的,而這些元素屬于某一個(gè)對(duì)象結(jié)構(gòu)。同時(shí)這個(gè)操作是在不改變各元素類的前提下,在這個(gè)前提下定義新操作是訪問者模式精髓中的精髓。
主要解決:穩(wěn)定的數(shù)據(jù)結(jié)構(gòu)和易變的操作耦合問題。就是把數(shù)據(jù)結(jié)構(gòu)和作用于結(jié)構(gòu)上的操作解耦合,使得操作集合可相對(duì)自由地演化。
本質(zhì):預(yù)留通路,回調(diào)實(shí)現(xiàn)。它的實(shí)現(xiàn)主要就是通過預(yù)先定義好調(diào)用的通路,在被訪問的對(duì)象上定義accept方法,在訪問者的對(duì)象上定義visit方法;然后在調(diào)用真正發(fā)生的時(shí)候,通過兩次分發(fā)的技術(shù),利用預(yù)先定義好的通路,回調(diào)到訪問者具體的實(shí)現(xiàn)上。
二、訪問者模式的結(jié)構(gòu)
Visitor抽象訪問者接口:它定義了對(duì)每一個(gè)元素(Element)訪問的行為,它的參數(shù)就是可以訪問的元素,它的方法個(gè)數(shù)理論上來(lái)講與元素個(gè)數(shù)(Element的實(shí)現(xiàn)類個(gè)數(shù))是一樣的,從這點(diǎn)不難看出,訪問者模式要求元素類的個(gè)數(shù)不能改變(不能改變的意思是說,如果元素類的個(gè)數(shù)經(jīng)常改變,則說明不適合使用訪問者模式)。
ConcreteVisitor具體訪問者角色:它需要給出對(duì)每一個(gè)元素類訪問時(shí)所產(chǎn)生的具體行為。
Element抽象節(jié)點(diǎn)(元素)角色:它定義了一個(gè)接受訪問者(accept)的方法,其意義是指,每一個(gè)元素都要可以被訪問者訪問。
ConcreteElement具體節(jié)點(diǎn)(元素)角色:它提供接受訪問方法的具體實(shí)現(xiàn),而這個(gè)具體的實(shí)現(xiàn),通常情況下是使用訪問者提供的訪問該元素類的方法。
ObjectStructure結(jié)構(gòu)對(duì)象角色:這個(gè)便是定義當(dāng)中所提到的對(duì)象結(jié)構(gòu),對(duì)象結(jié)構(gòu)是一個(gè)抽象表述,具體點(diǎn)可以理解為一個(gè)具有容器性質(zhì)或者復(fù)合對(duì)象特性的類,它會(huì)含有一組元素(Element),并且可以迭代這些元素,供訪問者訪問。
三、訪問者模式的使用場(chǎng)景
(1)對(duì)象結(jié)構(gòu)比較穩(wěn)定,但經(jīng)常需要在此對(duì)象結(jié)構(gòu)上定義新的操作。
(2)需要對(duì)一個(gè)對(duì)象結(jié)構(gòu)中的對(duì)象進(jìn)行很多不同的且不相關(guān)的操作,而需要避免這些操作“污染”這些對(duì)象的類,也不希望在增加新操作時(shí)修改這些類。
四、訪問者模式的優(yōu)缺點(diǎn)
優(yōu)點(diǎn):
1. 訪問者模式使得易于增加新的操作 訪問者使得增加依賴于復(fù)雜對(duì)象結(jié)構(gòu)的構(gòu)件的操作變得容易了。僅需增加一個(gè)新的訪問者即可在一個(gè)對(duì)象結(jié)構(gòu)上定義一個(gè)新的操作。相反, 如果每個(gè)功能都分散在多個(gè)類之上的話,定義新的操作時(shí)必須修改每一類。
2. 訪問者集中相關(guān)的操作而分離無(wú)關(guān)的操作 相關(guān)的行為不是分布在定義該對(duì)象結(jié)構(gòu)的 各個(gè)類上,而是集中在一個(gè)訪問者中。無(wú)關(guān)行為卻被分別放在它們各自的訪問者子類中。這 就既簡(jiǎn)化了這些元素的類,也簡(jiǎn)化了在這些訪問者中定義的算法。所有與它的算法相關(guān)的數(shù) 據(jù)結(jié)構(gòu)都可以被隱藏在訪問者中。
缺點(diǎn):
1. 增加新的 ConcreteElement類很困難
Visitor模式使得難以增加新的 Element的子類。每 添加一個(gè)新的 ConcreteElement都要在 Vistor中添加一個(gè)新的抽象操作,并在每一個(gè) ConcretVisitor類中實(shí)現(xiàn)相應(yīng)的操作。有時(shí)可以在 Visitor中提供一個(gè)缺省的實(shí)現(xiàn),這一實(shí)現(xiàn)可 以被大多數(shù)的 ConcreteVisitor繼承,但這與其說是一個(gè)規(guī)律還不如說是一種例外。
所以在應(yīng)用訪問者模式時(shí)考慮關(guān)鍵的問題是系統(tǒng)的哪個(gè)部分會(huì)經(jīng)常變化,是作用于對(duì)象結(jié)構(gòu)上的算法呢還是構(gòu)成該結(jié)構(gòu)的各個(gè)對(duì)象的類。如果老是有新的 ConcretElement類加入進(jìn)來(lái)的話, Vistor類層次將變得難以維護(hù)。在這種情況下,直接在構(gòu)成該結(jié)構(gòu)的類中定義這些操作可能更容易一些。如果 Element類層次是穩(wěn)定的,而你不斷地增加操作獲修改算法,訪問者模式可以幫助你管理這些改動(dòng)。
2. 破壞封裝
訪問者方法假定ConcreteElement接口的功能足夠強(qiáng),足以讓訪問者進(jìn)行它 們的工作。結(jié)果是,該模式常常迫使你提供訪問元素內(nèi)部狀態(tài)的公共操作,這可能會(huì)破壞它 的封裝性。
五、訪問者模式的實(shí)現(xiàn)
抽象訪問者角色:為每一個(gè)具體節(jié)點(diǎn)都準(zhǔn)備了一個(gè)訪問操作。
//這里由于有兩個(gè)節(jié)點(diǎn),因此,對(duì)應(yīng)就有兩個(gè)訪問操作。 public interface Visitor { /** * 對(duì)應(yīng)于NodeA的訪問操作 */ public void visit(NodeA node); /** * 對(duì)應(yīng)于NodeB的訪問操作 */ public void visit(NodeB node); }
具體訪問者
/** * 具體訪問者VisitorA類 */ public class VisitorA implements Visitor { /** * 對(duì)應(yīng)于NodeA的訪問操作 */ @Override public void visit(NodeA node) { System.out.println(node.operationA()); } /** * 對(duì)應(yīng)于NodeB的訪問操作 */ @Override public void visit(NodeB node) { System.out.println(node.operationB()); } } /** * 具體訪問者VisitorB類 */ public class VisitorB implements Visitor { /** * 對(duì)應(yīng)于NodeA的訪問操作 */ @Override public void visit(NodeA node) { System.out.println(node.operationA()); } /** * 對(duì)應(yīng)于NodeB的訪問操作 */ @Override public void visit(NodeB node) { System.out.println(node.operationB()); } }
抽象節(jié)點(diǎn)類
/** * 抽象節(jié)點(diǎn)類 */ public abstract class Node { /** * 接受操作 */ public abstract void accept(Visitor visitor); }
具體節(jié)點(diǎn)類
/** * 具體節(jié)點(diǎn)類NodeA */ public class NodeA extends Node { /** * 接受操作 */ @Override public void accept(Visitor visitor) { visitor.visit(this); } /** * NodeA特有的方法 */ public String operationA() { return "NodeA"; } } /** * 具體節(jié)點(diǎn)類NodeB */ public class NodeB extends Node { /** * 接受方法 */ @Override public void accept(Visitor visitor) { visitor.visit(this); } /** * NodeB特有的方法 */ public String operationB() { return "NodeB"; } }
結(jié)構(gòu)對(duì)象角色類
/** * 結(jié)構(gòu)對(duì)象角色類 * 這個(gè)結(jié)構(gòu)對(duì)象角色持有一個(gè)聚集,并向外界提供add()方法作為對(duì)聚集的管理操作。通過調(diào)用這個(gè)方法,可以動(dòng)態(tài)地增加一個(gè)新的節(jié)點(diǎn)。 */ public class ObjectStructure { private List<Node> nodes = new ArrayList<Node>(); /** * 執(zhí)行方法操作 */ public void action(Visitor visitor) { for (Node node : nodes) { node.accept(visitor); } } /** * 添加一個(gè)新元素 */ public void add(Node node) { nodes.add(node); } }
客戶端代碼
public static void main(String[] args) { //創(chuàng)建一個(gè)結(jié)構(gòu)對(duì)象 ObjectStructure os = new ObjectStructure(); //給結(jié)構(gòu)增加一個(gè)節(jié)點(diǎn) os.add(new NodeA()); //給結(jié)構(gòu)增加一個(gè)節(jié)點(diǎn) os.add(new NodeB()); //創(chuàng)建一個(gè)訪問者 Visitor visitor = new VisitorA(); os.action(visitor); }
總結(jié)
本篇文章就到這里了,希望能夠給你帶來(lái)幫助,也希望您能夠多多關(guān)注腳本之家的更多內(nèi)容!
相關(guān)文章
java實(shí)現(xiàn)文本框和文本區(qū)的輸入輸出
這篇文章主要介紹了java實(shí)現(xiàn)文本框和文本區(qū)的輸入輸出的方法和具體示例,有需要的小伙伴可以參考下。2015-06-06EntityWrapper如何在and條件中嵌套o(hù)r語(yǔ)句
這篇文章主要介紹了EntityWrapper如何在and條件中嵌套o(hù)r語(yǔ)句,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-03-03JAVA使用POI(XSSFWORKBOOK)讀取EXCEL文件過程解析
這篇文章主要介紹了JAVA使用POI(XSSFWORKBOOK)讀取EXCEL文件過程解析,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-08-08SpringBoot一個(gè)非常蛋疼的無(wú)法啟動(dòng)的問題解決
這篇文章主要介紹了SpringBoot一個(gè)非常蛋疼的無(wú)法啟動(dòng)的問題解決,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-11-11mybatis連接MySQL8出現(xiàn)的問題解決方法
這篇文章主要介紹了mybatis連接MySQL8出現(xiàn)的問題解決方法,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來(lái)看看吧2018-10-10java中利用List的subList方法實(shí)現(xiàn)對(duì)List分頁(yè)(簡(jiǎn)單易學(xué))
本篇文章主要介紹了java中l(wèi)ist數(shù)據(jù)拆分為sublist實(shí)現(xiàn)頁(yè)面分頁(yè)的簡(jiǎn)單代碼,具有一定的參考價(jià)值,有需要的可以了解一下。2016-11-11詳解java接口(interface)在不同JDK版本中的變化
這篇文章主要介紹了詳解java接口(interface)在不同JDK版本中的變化,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-02-02基于Java注解(Annotation)的自定義注解入門介紹
要深入學(xué)習(xí)注解,我們就必須能定義自己的注解,并使用注解,在定義自己的注解之前,我們就必須要了解Java為我們提供的元注解和相關(guān)定義注解的語(yǔ)法2013-04-04