• 极客专栏正式上线!欢迎访问 https://www.jikewenku.com/topic.html
  • 极客专栏正式上线!欢迎访问 https://www.jikewenku.com/topic.html

设计模式—访问者(Visitor)模式

技术杂谈 勤劳的小蚂蚁 2个月前 (02-07) 54次浏览 已收录 0个评论 扫描二维码

定义

访问者模式是一种从操作的对象结构中分离算法的方式。 它可以在不改变数据结构的前提下定义作用与这些元素的新操作。它遵循开闭原则。
Represent an operation to be performed on elements of an object structure. Visitor lets you define a new operation without changing the classes of the elements on which it operates.
visitor: n. 访问者,参观者;视察者.

涉及角色

  • Visitor 抽象访问者角色,为该对象结构中具体元素角色声明一个访问操作接口。该操作接口的名字和参数标识了发送访问请求给具体访问者的具体元素角色,这样访问者就可以通过该元素角色的特定接口直接访问它。
  • ConcreteVisitor.具体访问者角色,实现Visitor声明的接口。
  • Element 定义一个接受访问操作(accept()),它以一个访问者(Visitor)作为参数。
  • ConcreteElement 具体元素,实现了抽象元素(Element)所定义的接受操作接口。
  • ObjectStructure 结构对象角色,这是使用访问者模式必备的角色。它具备以下特性:能枚举它的元素;可以提供一个高层接口以允许访问者访问它的元素;如有需要,可以设计成一个复合对象或者一个聚集(如一个列表或无序集合)

通俗理解

  • 我作为一个访客(Visitor)到朋友家(Element)拜访,朋友之间喝喝酒,聊聊天,再互相吹捧。聊天的时候,朋友告诉我他今年的表现(doSomthing),然后我就做(visit-self-method)一些对这件事的评价。
  • 老板作为视察者,查阅(访问)手下员工的工作业绩。老板是Visitor的抽象实现,员工是Element的抽象实现。对象结构(Object Structure)为员工的业绩等信息
  • 家里有一台电脑,电脑出现了一点问题,那么我作为访问者,想去了解电脑的那个部分出了问题。我Visitor,电脑的各个部分(Element),查看有没有坏(visit method)
应该有很多类似的比喻,在开发的过程中多去思考,做什么事情都要思考

实现细节

  1. 定义一个表示Element的接口
  2. 实现Element接口。创建Element的实体类ConcreteElement
  3. 创建一个表示访问者Visitor的接口
  4. 实现Visitor的接口,创建Visitor实体类ConcreteVisitor,(有时候会有多个访问者)
  5. 使用Visitor实体类来访问Element。


特性


优点

  1. 符合单一职责原则
  2. 元素类可以通过接受不同的访问者来实现对不同操作的扩展。

缺点

  1. 具体元素对访问者公布细节,违背了迪米特法则。
  2. 违背了依赖倒置原则,访问者依赖的是具体元素,而不是抽象元素。

适用场景

  1. 对象结构中对象对应的类很少改变,但经常需要在此对象结构上定义新的操作
  2. 需要对一个对象结构中的对象进行很多不同的并且不相关的操作,而需要避免让这些操作”污染”这些对象的类,也不希望在增加新操作时修改这些类。

注意事项:访问者可以对功能进行统一,可以做报表、UI、拦截器与过滤器。


案例



案例一
类图

我们去检查汽车的各个部分是否能正常打印,使用Visitor根据不同的汽车部分来分发动作。 而不是在汽车的各个部分来打印。

interface CarElement {
    void accept(CarElementVisitor visitor);
}

interface CarElementVisitor {
    void visit(Body body);
    void visit(Car car);
    void visit(Engine engine);
    void visit(Wheel wheel);
}

class Car implements CarElement {
    CarElement[] elements;

    public Car() {
        this.elements = new CarElement[] {
            new Wheel(“front left”), new Wheel(“front right”),
            new Wheel(“back left”), new Wheel(“back right”),
            new Body(), new Engine()
        };
    }

    public void accept(final CarElementVisitor visitor) {
        for (CarElement elem : elements) {
            elem.accept(visitor);
        }
        visitor.visit(this);
    }
}

class Body implements CarElement {
    public void accept(final CarElementVisitor visitor) {
        visitor.visit(this);
    }
}

class Engine implements CarElement {
    public void accept(final CarElementVisitor visitor) {
        visitor.visit(this);
    }
}

class Wheel implements CarElement {
    private String name;

    public Wheel(final String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void accept(final CarElementVisitor visitor) {
        /*
         * accept(CarElementVisitor) in Wheel implements
         * accept(CarElementVisitor) in CarElement, so the call
         * to accept is bound at run time. This can be considered
         * the *first* dispatch. However, the decision to call
         * visit(Wheel) (as opposed to visit(Engine) etc.) can be
         * made during compile time since ‘this’ is known at compile
         * time to be a Wheel. Moreover, each implementation of
         * CarElementVisitor implements the visit(Wheel), which is
         * another decision that is made at run time. This can be
         * considered the *second* dispatch.
         */
        visitor.visit(this);
    }
}

class CarElementDoVisitor implements CarElementVisitor {
    public void visit(final Body body) {
        System.out.println(“Moving my body”);
    }

    public void visit(final Car car) {
        System.out.println(“Starting my car”);
    }

    public void visit(final Wheel wheel) {
        System.out.println(“Kicking my ” + wheel.getName() + ” wheel”);
    }

    public void visit(final Engine engine) {
        System.out.println(“Starting my engine”);
    }
}

class CarElementPrintVisitor implements CarElementVisitor {
    public void visit(final Body body) {
        System.out.println(“Visiting body”);
    }

    public void visit(final Car car) {
        System.out.println(“Visiting car”);
    }

    public void visit(final Engine engine) {
        System.out.println(“Visiting engine”);
    }

    public void visit(final Wheel wheel) {
        System.out.println(“Visiting ” + wheel.getName() + ” wheel”);
    }
}

public class VisitorDemo {
    public static void main(final String[] args) {
        final Car car = new Car();

        car.accept(new CarElementPrintVisitor());
        car.accept(new CarElementDoVisitor());
    }
}

/* 输出内容
Visiting front left wheel
Visiting front right wheel
Visiting back left wheel
Visiting back right wheel
Visiting body
Visiting engine
Visiting car
Kicking my front left wheel
Kicking my front right wheel
Kicking my back left wheel
Kicking my back right wheel
Moving my body
Starting my engine
Starting my car
*/

案例二

类图

本质上和案例一没什么差别
// ComputerPart.java
public interface ComputerPart {
   public void accept(ComputerPartVisitor computerPartVisitor);
}

// Keyboard.java
public class Keyboard  implements ComputerPart {

   @Override
   public void accept(ComputerPartVisitor computerPartVisitor) {
      computerPartVisitor.visit(this);
   }
}

// Monitor.java
public class Monitor  implements ComputerPart {

   @Override
   public void accept(ComputerPartVisitor computerPartVisitor) {
      computerPartVisitor.visit(this);
   }
}

// Mouse.java
public class Mouse  implements ComputerPart {

   @Override
   public void accept(ComputerPartVisitor computerPartVisitor) {
      computerPartVisitor.visit(this);
   }
}

// Computer.java
public class Computer implements ComputerPart {
    
   ComputerPart[] parts;

   public Computer(){
      parts = new ComputerPart[] {new Mouse(), new Keyboard(), new Monitor()};        
   } 


   @Override
   public void accept(ComputerPartVisitor computerPartVisitor) {
      for (int i = 0; i < parts.length; i++) {
         parts[i].accept(computerPartVisitor);
      }
      computerPartVisitor.visit(this);
   }
}

// ComputerPartVisitor
public interface ComputerPartVisitor {
    public void visit(Computer computer);
    public void visit(Mouse mouse);
    public void visit(Keyboard keyboard);
    public void visit(Monitor monitor);
}

// ComputerPartDisplayVisitor.java
public class ComputerPartDisplayVisitor implements ComputerPartVisitor {

   @Override
   public void visit(Computer computer) {
      System.out.println(“Displaying Computer.”);
   }

   @Override
   public void visit(Mouse mouse) {
      System.out.println(“Displaying Mouse.”);
   }

   @Override
   public void visit(Keyboard keyboard) {
      System.out.println(“Displaying Keyboard.”);
   }

   @Override
   public void visit(Monitor monitor) {
      System.out.println(“Displaying Monitor.”);
   }
}

// demo
public class VisitorPatternDemo {
   public static void main(String[] args) {

      ComputerPart computer = new Computer();
      computer.accept(new ComputerPartDisplayVisitor());
   }
}

/* 输出
Displaying Mouse.
Displaying Keyboard.
Displaying Monitor.
Displaying Computer.
*/

小结

主要记录了在学习设计模式时的一些资料,对资料进行了整理。想要深入理解设计模式,还要多读优秀的代码,在开发的时候多去思考相关的应用场景。

附录

  • 访问者模式—百度百科:https://0x9.me/HXKTA(短网址)
  • 访问者模式—维基百科:https://en.wikipedia.org/wiki/Visitor_pattern
  • 访问者模式—菜鸟教程:http://www.runoob.com/design-pattern/visitor-pattern.html


丨极客文库, 版权所有丨如未注明 , 均为原创丨
本网站采用知识共享署名-非商业性使用-相同方式共享 3.0 中国大陆许可协议进行授权
转载请注明原文链接:设计模式—访问者(Visitor)模式
喜欢 (0)
[247507792@qq.com]
分享 (0)
勤劳的小蚂蚁
关于作者:
温馨提示:本文来源于网络,转载文章皆标明了出处,如果您发现侵权文章,请及时向站长反馈删除。

您必须 登录 才能发表评论!

  • 精品技术教程
  • 编程资源分享
  • 问答交流社区
  • 极客文库知识库

客服QQ


QQ:2248886839


工作时间:09:00-23:00