一、概述
1.1 定義
訪問者設計模式是一種行為型設計模式,用于將算法與對象結構分離。它允許你在不改變對象結構的前提下定義新的操作。
1.2 作用
訪問者模式的作用是在不改變對象結構的前提下定義新的操作。它允許你定義一個新的操作,而無需修改現有的對象結構。在訪問者模式中,我們將操作封裝在訪問者對象中,并在元素對象上調用訪問者對象的方法,從而實現對元素對象的操作。
1.3 適用場景
訪問者模式適用于以下場景:
?對象結構穩定,但是經常需要在此結構上定義新的操作;?需要對復雜對象結構中的對象進行操作,而且這些對象可能具有不同的類型;?需要在不改變對象結構的前提下,為對象結構中的元素對象動態添加新的操作。
二、角色
2.1 抽象訪問者(Visitor)
抽象訪問者(Visitor)定義了訪問者可以訪問的元素對象的接口。它包含了多個 visit() 方法,每個方法對應一個具體元素對象。
publicinterfaceVisitor{
void visit(ConcreteElementA elementA);
void visit(ConcreteElementB elementB);
}
在上述代碼中,我們定義了一個抽象訪問者接口 Visitor,它包含了兩個 visit() 方法,分別對應具體元素對象 ConcreteElementA 和 ConcreteElementB。
2.2 具體訪問者(ConcreteVisitor)
具體訪問者(ConcreteVisitor)實現了抽象訪問者接口,對不同類型的元素對象進行具體的操作。
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");
}
}
在上述代碼中,我們定義了兩個具體訪問者類 ConcreteVisitorA 和 ConcreteVisitorB,它們分別實現了抽象訪問者接口 Visitor,并對不同類型的元素對象進行具體的操作。
2.3 抽象元素(Element)
抽象元素(Element)定義了元素對象的接口,讓訪問者對象可以訪問自己的元素對象。
publicinterfaceElement{
void accept(Visitor visitor);
}
在上述代碼中,我們定義了一個抽象元素接口 Element,它包含了一個 accept() 方法,該方法接收一個訪問者對象作為參數。
2.4 具體元素(ConcreteElement)
具體元素(ConcreteElement)實現了抽象元素接口,定義了自己的 accept() 方法,該方法調用訪問者對象的 visit() 方法,并將自身作為參數傳入。
publicclassConcreteElementAimplementsElement{
@Override
publicvoid accept(Visitor visitor){
visitor.visit(this);
}
}
publicclassConcreteElementBimplementsElement{
@Override
publicvoid accept(Visitor visitor){
visitor.visit(this);
}
}
在上述代碼中,我們定義了兩個具體元素類 ConcreteElementA 和 ConcreteElementB,它們分別實現了抽象元素接口 Element,并在 accept() 方法中調用訪問者對象的 visit() 方法,并將自身作為參數傳入。
2.5 對象結構(Object Structure)
對象結構(Object Structure)是元素對象的集合,它提供了一個接口,讓訪問者對象可以訪問集合中的元素對象。
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);
}
}
}
在上述代碼中,我們定義了一個對象結構類 ObjectStructure,它包含了一個元素對象的集合 elements,提供了 attach() 和 detach() 方法,用于添加和刪除元素對象。它還提供了一個 accept() 方法,該方法遍歷元素對象集合,并調用每個元素對象的accept() 方法,將訪問者對象作為參數傳入。
三、實現步驟
3.1 創建抽象元素類
publicinterfaceElement{
void accept(Visitor visitor);
}
3.2 創建具體元素類
publicclassConcreteElementAimplementsElement{
@Override
publicvoid accept(Visitor visitor){
visitor.visit(this);
}
}
publicclassConcreteElementBimplementsElement{
@Override
publicvoid accept(Visitor visitor){
visitor.visit(this);
}
}
3.3 創建抽象訪問者類
publicinterfaceVisitor{
void visit(ConcreteElementA elementA);
void visit(ConcreteElementB elementB);
}
3.4 創建具體訪問者類
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 創建對象結構類
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 客戶端調用
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,并向其中添加了兩個元素對象 ConcreteElementA 和 ConcreteElementB。然后,我們創建了兩個具體訪問者對象 visitorA 和 visitorB,并調用 objectStructure 的 accept() 方法,將這兩個訪問者對象作為參數傳入。在 accept() 方法中,我們遍歷元素對象集合 elements,并依次調用每個元素對象的 accept() 方法,將訪問者對象作為參數傳入。
四、優缺點
4.1 優點
訪問者設計模式的優點包括:
?可以在不改變對象結構的前提下定義新的操作;?可以將代碼的穩定性和易于擴展性相結合;?可以使得增加新的操作變得簡單。
4.2 缺點
訪問者設計模式的缺點包括:
?增加新的元素對象比較困難;?元素對象的訪問者接口必須穩定,否則會導致所有訪問者對象都需要進行修改;?具體元素對象對訪問者對象的訪問是單向的,訪問者對象無法訪問元素對象的其他方法。
五、擴展點
5.1 雙重分派
在訪問者設計模式中,我們可以通過雙重分派來實現不同的操作。雙重分派是指在訪問者對象中定義了多個具體的 visit() 方法,每個方法對應一個具體元素對象,然后在元素對象中調用訪問者對象的 visit() 方法,并將自身作為參數傳入。
雙重分派的實現方式是通過重載 accept() 方法來實現的。具體來說,我們在抽象元素接口 Element 中定義多個 accept() 方法,每個方法對應一個具體訪問者對象,并在具體元素對象中重載這些 accept() 方法,將具體訪問者對象作為參數傳入。
下面是一個雙重分派的示例代碼:
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");
}
}