介绍

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

原理image-20201021131723007.png

对原理类图的说明-即(访问者模式的角色与职责)

  1. Visitor 是抽象访问者,为该对象结构中的ConcreteElement的每一个类声明一个visit操作
  2. ConcreteVisitor :是一个具体的访问值 实现每个有Visitor 声明的操作,是每个操作实现的部分.
  3. ObjectStructure 能枚举它的元素, 可以提供一个高层的接口,用来允许访问者访问元素
  4. Element 定义一个accept 方法,接收一个访问者对象
  5. ConcreteElement 为具体元素,实现了accept 方法

案例

完成测评系统需求:
将观众分为男人和女人,对歌手进行测评,当看完某个歌手表演后,得到他们对该歌手不同的评价(评价有不同的种类,比如成功、失败等)
image-20201021131929669.png

  1. //抽象动作类(作用:通过实现类预先把能做出的评价模板做好,需要时直接调用)
  2. public interface Action {
  3. void getManResult(Man man);
  4. void getWomenResult(Woman woman);
  5. }
  6. //成功评价
  7. public class Success implements Action{
  8. @Override
  9. public void getManResult(Man man) {
  10. System.out.println(man.getName()+"先生给出了成功的评价");
  11. }
  12. @Override
  13. public void getWomenResult(Woman woman) {
  14. System.out.println(woman.getName()+"女士给出了成功的评价");
  15. }
  16. }
  17. //失败评价
  18. public class Fail implements Action{
  19. @Override
  20. public void getManResult(Man man) {
  21. System.out.println(man.getName()+"先生给出了失败的评价");
  22. }
  23. @Override
  24. public void getWomenResult(Woman woman) {
  25. System.out.println(woman.getName()+"女士给出了失败的评价");
  26. }
  27. }
  28. //当需要扩展评价种类时,直接扩展一个待定评价即可,其他代码不需要修改
  29. public class Wait implements Action{
  30. @Override
  31. public void getManResult(Man man) {
  32. System.out.println(man.getName()+"先生给出了待定---");
  33. }
  34. @Override
  35. public void getWomenResult(Woman woman) {
  36. System.out.println(woman.getName()+"女士给出了待定---");
  37. }
  38. }
  1. //抽象评价人类
  2. public abstract class Person {
  3. private String name;
  4. public Person(String name) {
  5. this.name = name;
  6. }
  7. public String getName() {
  8. return name;
  9. }
  10. public void setName(String name) {
  11. this.name = name;
  12. }
  13. public abstract void accept(Action action); //选择一个已经写好的评价
  14. }
  15. //男人
  16. public class Man extends Person{
  17. public Man(String name) {
  18. super(name);
  19. }
  20. @Override
  21. public void accept(Action action) {
  22. action.getManResult(this); //选择一个已经写好的评价,并赋予评价自己的特性
  23. //举例: 写好的评价:成功 予评价自己的特性:小明给的成功
  24. }
  25. }
  26. //女人
  27. public class Woman extends Person{
  28. public Woman(String name) {
  29. super(name);
  30. }
  31. @Override
  32. public void accept(Action action) {
  33. action.getWomenResult(this);
  34. }
  35. }
  1. //使用集合管理所有评价人
  2. public class ObjectStructure {
  3. private ArrayList<Person> persons = new ArrayList<>();
  4. public void addPerson(Person p){
  5. persons.add(p);
  6. }
  7. public void removePerson(Person p){
  8. persons.remove(p);
  9. }
  10. public void display(Action action){
  11. for (Person person : persons) {
  12. person.accept(action);
  13. }
  14. }
  15. }
  1. //client端
  2. public class Client {
  3. public static void main(String[] args) {
  4. //把评价交给集合类管理
  5. ObjectStructure objectStructure = new ObjectStructure();
  6. objectStructure.addPerson(new Man("张三"));
  7. objectStructure.addPerson(new Woman("小红"));
  8. objectStructure.display(new Success());
  9. objectStructure.display(new Fail());
  10. objectStructure.display(new Wait());
  11. }
  12. }

image-20201021132834800.png

访问者模式的注意事项和细节

优点

  1. 访问者模式符合单一职责原则、让程序具有优秀的扩展性、灵活性非常高
  2. 访问者模式可以对功能进行统一,可以做报表、UI、拦截器与过滤器,适用于数据结构相对稳定的系统

缺点

  1. 具体元素对访问者公布细节,也就是说访问者关注了其他类的内部细节,这是迪米特法则所不建议的, 这样造成了具体元素变更比较困难
  2. 违背了依赖倒转原则。访问者依赖的是具体元素,而不是抽象元素
  3. 因此,如果一个系统有比较稳定的数据结构,又有经常变化的功能需求,那么访问者模式就是比较合适的.