一、概述
1.1 定義
訪問(wèn)者設(shè)計(jì)模式是一種行為型設(shè)計(jì)模式,用于將算法與對(duì)象結(jié)構(gòu)分離。它允許你在不改變對(duì)象結(jié)構(gòu)的前提下定義新的操作。
1.2 作用
訪問(wèn)者模式的作用是在不改變對(duì)象結(jié)構(gòu)的前提下定義新的操作。它允許你定義一個(gè)新的操作,而無(wú)需修改現(xiàn)有的對(duì)象結(jié)構(gòu)。在訪問(wèn)者模式中,我們將操作封裝在訪問(wèn)者對(duì)象中,并在元素對(duì)象上調(diào)用訪問(wèn)者對(duì)象的方法,從而實(shí)現(xiàn)對(duì)元素對(duì)象的操作。
1.3 適用場(chǎng)景
訪問(wèn)者模式適用于以下場(chǎng)景:
?對(duì)象結(jié)構(gòu)穩(wěn)定,但是經(jīng)常需要在此結(jié)構(gòu)上定義新的操作;?需要對(duì)復(fù)雜對(duì)象結(jié)構(gòu)中的對(duì)象進(jìn)行操作,而且這些對(duì)象可能具有不同的類型;?需要在不改變對(duì)象結(jié)構(gòu)的前提下,為對(duì)象結(jié)構(gòu)中的元素對(duì)象動(dòng)態(tài)添加新的操作。
二、角色
2.1 抽象訪問(wèn)者(Visitor)
抽象訪問(wèn)者(Visitor)定義了訪問(wèn)者可以訪問(wèn)的元素對(duì)象的接口。它包含了多個(gè) visit() 方法,每個(gè)方法對(duì)應(yīng)一個(gè)具體元素對(duì)象。
publicinterfaceVisitor{
void visit(ConcreteElementA elementA);
void visit(ConcreteElementB elementB);
}
在上述代碼中,我們定義了一個(gè)抽象訪問(wèn)者接口 Visitor,它包含了兩個(gè) visit() 方法,分別對(duì)應(yīng)具體元素對(duì)象 ConcreteElementA 和 ConcreteElementB。
2.2 具體訪問(wèn)者(ConcreteVisitor)
具體訪問(wèn)者(ConcreteVisitor)實(shí)現(xiàn)了抽象訪問(wèn)者接口,對(duì)不同類型的元素對(duì)象進(jìn)行具體的操作。
publicclassConcreteVisitorAimplementsVisitor{
@Override
publicvoid visit(ConcreteElementA elementA){
System.out.println("ConcreteVisitorA visit ConcreteElementA");
}
@Override
publicvoid visit(ConcreteElementB elementB){
System.out.println("ConcreteVisitorA visit ConcreteElementB");
}
}
publicclassConcreteVisitorBimplementsVisitor{
@Override
publicvoid visit(ConcreteElementA elementA){
System.out.println("ConcreteVisitorB visit ConcreteElementA");
}
@Overridepublicvoid visit(ConcreteElementB elementB){
System.out.println("ConcreteVisitorB visit ConcreteElementB");
}
}
在上述代碼中,我們定義了兩個(gè)具體訪問(wèn)者類 ConcreteVisitorA 和 ConcreteVisitorB,它們分別實(shí)現(xiàn)了抽象訪問(wèn)者接口 Visitor,并對(duì)不同類型的元素對(duì)象進(jìn)行具體的操作。
2.3 抽象元素(Element)
抽象元素(Element)定義了元素對(duì)象的接口,讓訪問(wèn)者對(duì)象可以訪問(wèn)自己的元素對(duì)象。
publicinterfaceElement{
void accept(Visitor visitor);
}
在上述代碼中,我們定義了一個(gè)抽象元素接口 Element,它包含了一個(gè) accept() 方法,該方法接收一個(gè)訪問(wèn)者對(duì)象作為參數(shù)。
2.4 具體元素(ConcreteElement)
具體元素(ConcreteElement)實(shí)現(xiàn)了抽象元素接口,定義了自己的 accept() 方法,該方法調(diào)用訪問(wèn)者對(duì)象的 visit() 方法,并將自身作為參數(shù)傳入。
publicclassConcreteElementAimplementsElement{
@Override
publicvoid accept(Visitor visitor){
visitor.visit(this);
}
}
publicclassConcreteElementBimplementsElement{
@Override
publicvoid accept(Visitor visitor){
visitor.visit(this);
}
}
在上述代碼中,我們定義了兩個(gè)具體元素類 ConcreteElementA 和 ConcreteElementB,它們分別實(shí)現(xiàn)了抽象元素接口 Element,并在 accept() 方法中調(diào)用訪問(wèn)者對(duì)象的 visit() 方法,并將自身作為參數(shù)傳入。
2.5 對(duì)象結(jié)構(gòu)(Object Structure)
對(duì)象結(jié)構(gòu)(Object Structure)是元素對(duì)象的集合,它提供了一個(gè)接口,讓訪問(wèn)者對(duì)象可以訪問(wèn)集合中的元素對(duì)象。
publicclassObjectStructure{
privateList< Element > elements =newArrayList< >();
publicvoid attach(Element element){
elements.add(element);
}
publicvoid detach(Element element){
elements.remove(element);
}
publicvoid accept(Visitor visitor){
for(Element element : elements){
element.accept(visitor);
}
}
}
在上述代碼中,我們定義了一個(gè)對(duì)象結(jié)構(gòu)類 ObjectStructure,它包含了一個(gè)元素對(duì)象的集合 elements,提供了 attach() 和 detach() 方法,用于添加和刪除元素對(duì)象。它還提供了一個(gè) accept() 方法,該方法遍歷元素對(duì)象集合,并調(diào)用每個(gè)元素對(duì)象的accept() 方法,將訪問(wèn)者對(duì)象作為參數(shù)傳入。
三、實(shí)現(xiàn)步驟
3.1 創(chuàng)建抽象元素類
publicinterfaceElement{
void accept(Visitor visitor);
}
3.2 創(chuàng)建具體元素類
publicclassConcreteElementAimplementsElement{
@Override
publicvoid accept(Visitor visitor){
visitor.visit(this);
}
}
publicclassConcreteElementBimplementsElement{
@Override
publicvoid accept(Visitor visitor){
visitor.visit(this);
}
}
3.3 創(chuàng)建抽象訪問(wèn)者類
publicinterfaceVisitor{
void visit(ConcreteElementA elementA);
void visit(ConcreteElementB elementB);
}
3.4 創(chuàng)建具體訪問(wèn)者類
publicclassConcreteVisitorAimplementsVisitor{
@Override
publicvoid visit(ConcreteElementA elementA){
System.out.println("ConcreteVisitorA visit ConcreteElementA");
}
@Override
publicvoid visit(ConcreteElementB elementB){
System.out.println("ConcreteVisitorA visit ConcreteElementB");
}
}
publicclassConcreteVisitorBimplementsVisitor{
@Override
publicvoid visit(ConcreteElementA elementA){
System.out.println("ConcreteVisitorB visit ConcreteElementA");
}
@Override
publicvoid visit(ConcreteElementB elementB){
System.out.println("ConcreteVisitorB visit ConcreteElementB");
}
}
3.5 創(chuàng)建對(duì)象結(jié)構(gòu)類
publicclassObjectStructure{
privateList< Element > elements =newArrayList< >();
publicvoid attach(Element element){
elements.add(element);
}
publicvoid detach(Element element){
elements.remove(element);
}
publicvoid accept(Visitor visitor){
for(Element element : elements){
element.accept(visitor);
}
}
}
3.6 客戶端調(diào)用
publicclassClient{
publicstaticvoid main(String[] args){
ObjectStructure objectStructure =newObjectStructure();
objectStructure.attach(newConcreteElementA());
objectStructure.attach(newConcreteElementB());
Visitor visitorA =newConcreteVisitorA();
Visitor visitorB =newConcreteVisitorB();
objectStructure.accept(visitorA);
objectStructure.accept(visitorB);
}
}
在上述代碼中,我們創(chuàng)建了一個(gè)對(duì)象結(jié)構(gòu) objectStructure,并向其中添加了兩個(gè)元素對(duì)象 ConcreteElementA 和 ConcreteElementB。然后,我們創(chuàng)建了兩個(gè)具體訪問(wèn)者對(duì)象 visitorA 和 visitorB,并調(diào)用 objectStructure 的 accept() 方法,將這兩個(gè)訪問(wèn)者對(duì)象作為參數(shù)傳入。在 accept() 方法中,我們遍歷元素對(duì)象集合 elements,并依次調(diào)用每個(gè)元素對(duì)象的 accept() 方法,將訪問(wèn)者對(duì)象作為參數(shù)傳入。
四、優(yōu)缺點(diǎn)
4.1 優(yōu)點(diǎn)
訪問(wèn)者設(shè)計(jì)模式的優(yōu)點(diǎn)包括:
?可以在不改變對(duì)象結(jié)構(gòu)的前提下定義新的操作;?可以將代碼的穩(wěn)定性和易于擴(kuò)展性相結(jié)合;?可以使得增加新的操作變得簡(jiǎn)單。
4.2 缺點(diǎn)
訪問(wèn)者設(shè)計(jì)模式的缺點(diǎn)包括:
?增加新的元素對(duì)象比較困難;?元素對(duì)象的訪問(wèn)者接口必須穩(wěn)定,否則會(huì)導(dǎo)致所有訪問(wèn)者對(duì)象都需要進(jìn)行修改;?具體元素對(duì)象對(duì)訪問(wèn)者對(duì)象的訪問(wèn)是單向的,訪問(wèn)者對(duì)象無(wú)法訪問(wèn)元素對(duì)象的其他方法。
五、擴(kuò)展點(diǎn)
5.1 雙重分派
在訪問(wèn)者設(shè)計(jì)模式中,我們可以通過(guò)雙重分派來(lái)實(shí)現(xiàn)不同的操作。雙重分派是指在訪問(wèn)者對(duì)象中定義了多個(gè)具體的 visit() 方法,每個(gè)方法對(duì)應(yīng)一個(gè)具體元素對(duì)象,然后在元素對(duì)象中調(diào)用訪問(wèn)者對(duì)象的 visit() 方法,并將自身作為參數(shù)傳入。
雙重分派的實(shí)現(xiàn)方式是通過(guò)重載 accept() 方法來(lái)實(shí)現(xiàn)的。具體來(lái)說(shuō),我們?cè)诔橄笤亟涌?Element 中定義多個(gè) accept() 方法,每個(gè)方法對(duì)應(yīng)一個(gè)具體訪問(wèn)者對(duì)象,并在具體元素對(duì)象中重載這些 accept() 方法,將具體訪問(wèn)者對(duì)象作為參數(shù)傳入。
下面是一個(gè)雙重分派的示例代碼:
publicinterfaceElement{
void accept(Visitor visitor);
void accept(Visitor visitor,String param);
}
publicclassConcreteElementAimplementsElement{
@Override
publicvoid accept(Visitor visitor){
visitor.visit(this);
}
@Override
publicvoid accept(Visitor visitor,String param){
visitor.visit(this, param);
}
}
publicinterfaceVisitor{
void visit(ConcreteElementA elementA);
void visit(ConcreteElementB elementB);
void visit(ConcreteElementA elementA,String param);
}
publicclassConcreteVisitorAimplementsVisitor{
@Override
publicvoid visit(ConcreteElementA elementA){
System.out.println("ConcreteVisitorA visit ConcreteElementA");
}
@Override
publicvoid visit(ConcreteElementB elementB){
System.out.println("ConcreteVisitorA visit ConcreteElementB");
}
@Override
publicvoid visit(ConcreteElementA elementA,String param){
System.out.println("ConcreteVisitorA visit ConcreteElementA with param "+ param);
}
}
publicclassConcreteVisitorBimplementsVisitor{
@Override
publicvoid visit(ConcreteElementA elementA){
System.out.println("ConcreteVisitorB visit ConcreteElementA");
}
@Override
publicvoid visit(ConcreteElementB elementB){
System.out.println("ConcreteVisitorB visit ConcreteElementB");
}
@Override
publicvoid visit(ConcreteElementA elementA,String param){
System.out.println("ConcreteVisitorB visit ConcreteElementA with param "+ param);
}
}
publicclassObjectStructure{
privateList< Element > elements =newArrayList< >();
publicvoid attach(Element element){
elements.add(element);
}
publicvoid detach(Element element){
elements.remove(element);
}
publicvoid accept(Visitor visitor){
for(Element element : elements){
element.accept(visitor);
}
}
publicvoid accept(Visitor visitor,String param){
for(Element element : elements){
element.accept(visitor, param);
}
}
}
publicclassClient{
publicstaticvoid main(String[] args){
ObjectStructure objectStructure =newObjectStructure();
objectStructure.attach(newConcreteElementA());
objectStructure.attach(newConcreteElementB());
Visitor visitorA =newConcreteVisitorA();
Visitor visitorB =newConcreteVisitorB();
objectStructure.accept(visitorA);
objectStructure.accept(visitorB);
objectStructure.accept(visitorA,"paramA");
objectStructure.accept(visitorB,"paramB");
}
}