设计模式:访问者设计模式

电子说

1.2w人已加入

描述

一、概述

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");
}
}

在上述代码中,我们定义了一个新的 accept() 方法,该方法接收一个额外的参数 param,并在具体元素对象的 accept() 方法中将该参数传递给访问者对象的 visit() 方法。然后,我们创建了两个具体访问者对象 visitorA 和 visitorB,并分别调用 objectStructure 的 accept() 方法,将这两个访问者对象作为参数传入,并在第二个 accept() 方法中传递了参数 "paramA" 和 "paramB"。在访问者对象的 visit() 方法中,我们可以根据传递的参数进行不同的操作。

双重分派的优点是可以根据传递的参数实现不同的操作,从而增强了访问者模式的灵活性和扩展性。

然而,双重分派也有一些缺点。首先,它会导致类的层次结构变得复杂,因为每个具体元素对象都需要实现多个 accept() 方法。其次,双重分派会增加代码的复杂性,因为需要在访问者对象中定义多个具体的 visit() 方法,并在具体元素对象中重载多个 accept() 方法。最后,双重分派可能会导致代码的可读性变差,因为需要理解多个层次的调用关系。

因此,在使用双重分派时需要谨慎考虑,避免过度使用,以免导致代码的复杂性和可维护性下降。

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分