深入理解23种设计模式(14) -- 访问者模式

274 阅读4分钟

在这里插入图片描述

介绍

  1. 访问者模式 (Visitor Pattern) : 封装一些作用于某种数据结构的各元素操作,它可以在不改变数据结构的前提下定义作用于这些元素新的操作。
  2. 主要将数据结构与数据操作分离,解决数据结构和操作耦合性问题
  3. 访问者模式基本工作原理是 :在被访问的类里面添加一个对外提供接待访问者的接口
  4. 访问者模式应用场景:需要对一个对象结构中对象进行很多不同操作(这些操作彼此没有关联),同时需要避免让这些操作 "污染" 这些类的对象,可以选用访问者模式。

在这里插入图片描述

  • Visitor:接口或者抽象类,定义了对每个 Element 访问的行为,它的参数就是被访问的元素,它的方法个数理论上与元素的个数是一样的,因此,访问者模式要求元素的类型要稳定,如果经常添加、移除元素类,必然会导致频繁地修改 Visitor 接口,如果出现这种情况,则说明不适合使用访问者模式。
  • ConcreteVisitor:具体的访问者,它需要给出对每一个元素类访问时所产生的具体行为。
  • Element:元素接口或者抽象类,它定义了一个接受访问者(accept)的方法,其意义是指每一个元素都要可以被访问者访问。
  • ConcreteElement:具体的元素类,它提供接受访问的具体实现,而这个具体的实现,通常情况下是使用访问者提供的访问该元素类的方法。
  • ObjectStructure:定义当中所提到的对象结构,对象结构是一个抽象表述,它内部管理了元素集合,并且可以迭代这些元素提供访问者访问。

案例

测评系统需求

  • 将观众分为男人和女人,对歌手进行测评,当看完某个选手当表演后,得到他们对该歌手不同当评价(评价有不同的种类 :好、失败)
  1. 创建行动抽象类 Action
public abstract class Action {

    /**
     * 得到男性的测评
     * @param man
     */
    public abstract void getManReslt(Man man);

    /**
     * 得到女性的测评
     * @param woman
     */
     public abstract void getWomanReslt(Woman woman);
}
  1. 创建成功类继承Action
public class Success extends Action{
    @Override
    public void getManReslt(Man man) {
        System.out.printf("男人"+man.getName()+"对该歌手评价是很成功");
    }

    @Override
    public void getWomanReslt(Woman woman) {
        System.out.printf("女人"+woman.getName()+"对该歌手评价是很成功");
    }
}
  1. 创建失败类继承Action
public class Fial extends Action{
    @Override
    public void getManReslt(Man man) {
        System.out.printf("男人"+man.getName()+"对该歌手评价是很失败");
    }

    @Override
    public void getWomanReslt(Woman woman) {
        System.out.printf("女人"+woman.getName()+"对该歌手评价是很失败");
    }
}
  1. 创建用户 抽象类person
public abstract class Person {

    /**
     * 让别人可以访问
     * @param action
     */
    public abstract void accept(Action action);
}
  1. 创建男人 继承Person
@Data
public class Man extends Person{

    private String name;

    public Man(String name) {
        this.name = name;
    }

    @Override
    public void accept(Action action) {
        action.getManReslt(this);
    }
}
  1. 创建女人继承Person
@Data
public class Woman extends Person{

    private String name;

    public Woman(String name) {
        this.name = name;
    }

    @Override
    public void accept(Action action) {
        action.getWomanReslt(this);
    }
}
  1. 创建ObjectStructure
public class ObjectStructure {


    public List<Person> personList = new LinkedList<>();

    /**
     * 添加
     * @param person
     */
    public void attach(Person person){
        personList.add(person);
    }

    /**
     * 移除
     * @param person
     */
    public void detach(Person person){
        personList.remove(person);
    }

    /**
     * 显示测评情况
     * @param action
     */
    public void display(Action action){
        personList.stream().forEach(person -> {
            person.accept(action);
        });
    }
}

  1. 客户端
public class Client {

    public static void main(String[] args) {
        ObjectStructure objectStructure = new ObjectStructure();
        objectStructure.attach(new Man("大家"));
        objectStructure.attach(new Woman("李芳"));

        Success success = new Success();
        objectStructure.display(success);
        System.out.printf("\n");

        Fial fial = new Fial();
        objectStructure.display(fial);
    }
}

在这里插入图片描述

扩展

双分派:所谓双分派指不管类如何变化,我们都能找到期望的方法运行,双分派意味着得到执行的操作取决于请求的种类和两个接收者类型

  • 访问者模式的优点。
  1. 各角色职责分离,符合单一职责原则 通过UML类图和上面的示例可以看出来,Visitor、ConcreteVisitor、Element 、ObjectStructure,职责单一,各司其责。
  2. 具有优秀的扩展性
  3. 如果需要增加新的访问者,增加实现类 ConcreteVisitor 就可以快速扩展。 使得数据结构和作用于结构上的操作解耦,使得操作集合可以独立变化
  4. 灵活性
  • 访问者模式的缺点。
  1. 具体元素对访问者公布细节,违反了迪米特原则

  2. 具体元素变更时导致修改成本大

  3. 违反了依赖倒置原则,为了达到“区别对待”而依赖了具体类,没有以来抽象 访问者 visit 方法中,依赖了具体方法。



github Demo地址 : ~~~传送门~~~

个人博客地址:blog.yanxiaolong.cn/