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