简说设计模式——访问者模式

一、什么是访问者模式

  访问者模式是一个相对比较简单,但结构又稍显复杂的模式,它讲的是表示一个作用于某对象结构中的各元素的操作,它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作。例如,你在朋友家做客,你是访问者,朋友接收你的访问,你通过朋友的描述,然后对朋友的描述做出一个判断,这就是访问者模式。

  访问者模式(Visitor),封装一些作用于某种数据结构的各元素的操作,它可以在不改变数据结构的前提下定义作用于这些元素的新的操作。UML结构图如下:

  其中,Visitor是抽象访问者,为该对象结构中ConcreteElement的每一个类声明一个Visit操作;ConcreteVisitor是具体访问者,实现每个由visitor声明的操作,是每个操作实现算法的一部分,而该算法片段是对应于结构中对象的类;ObjectStructure为能枚举它的元素,可以提供一个高层的接口以允许访问者访问它的元素;Element定义了一个Accept操作,它以一个访问者为参数;ConcreteElement为具体元素,实现Accept操作。

  1. 抽象访问者

  此处可为抽象类或接口,用于声明访问者可以访问哪些元素,具体到程序中就是visit方法的参数定义哪些对象是可以被访问的。

1 public abstract class Visitor {
2
3     public abstract void visitConcreteElementA(ConcreteElementA concreteElementA);
4
5     public abstract void visitConcreteElementB(ConcreteElementB concreteElementB);
6
7 }

  2. 具体访问者

  影响访问者访问到一个类后该干什么、怎么干。这里以ConcreteVisitor1为例,ConcreteVisitor2就不再赘述了。

 1 public class ConcreteVisitor1 extends Visitor {
 2
 3     @Override
 4     public void visitConcreteElementA(ConcreteElementA concreteElementA) {
 5         System.out.println(concreteElementA.getClass().getName() + " 被 " + this.getClass().getName() + " 访问");
 6     }
 7
 8     @Override
 9     public void visitConcreteElementB(ConcreteElementB concreteElementB) {
10         System.out.println(concreteElementB.getClass().getName() + " 被 " + this.getClass().getName() + " 访问");
11     }
12
13 }

  3. 抽象元素

  此处为接口后抽象类,用于声明接受哪一类访问者访问,程序上是通过accpet方法中的参数来定义的。

  抽象元素有两类方法,一是本身的业务逻辑,也就是元素作为一个业务处理单元必须完成的职责;另外一个是允许哪一个访问者来访问。这里只声明的第二类即accept方法。

1 public abstract class Element {
2
3     public abstract void accept(Visitor visitor);
4
5 }

  4. 具体元素

  实现accept方法,通常是visitor.visit(this)。这里以ConcreteElementA为例,ConcreteElementB就不再赘述了。

 1 public class ConcreteElementA extends Element {
 2
 3     @Override
 4     public void accept(Visitor visitor) {
 5         visitor.visitConcreteElementA(this);
 6     }
 7
 8     //其它方法
 9     public void operationA() {
10
11     }
12
13 }

  5. 结构对象

  元素生产者,一般容纳在多个不同类、不同接口的容器,如List、Set、Map等,在项目中,一般很少抽象出这个角色。

 1 public class ObjectStructure {
 2
 3     private List<Element> elements = new LinkedList<>();
 4
 5     public void attach(Element element) {
 6         elements.add(element);
 7     }
 8
 9     public void detach(Element element) {
10         elements.remove(element);
11     }
12
13     public void accept(Visitor visitor) {
14         for (Element element : elements) {
15             element.accept(visitor);
16         }
17     }
18
19 }

  6. Client客户端

  我们通过以下场景模拟一下访问者模式。

 1 public class Client {
 2
 3     public static void main(String[] args) {
 4         ObjectStructure objectStructure = new ObjectStructure();
 5
 6         objectStructure.attach(new ConcreteElementA());
 7         objectStructure.attach(new ConcreteElementB());
 8
 9         ConcreteVisitor1 visitor1 = new ConcreteVisitor1();
10         ConcreteVisitor2 visitor2 = new ConcreteVisitor2();
11
12         objectStructure.accept(visitor1);
13         objectStructure.accept(visitor2);
14     }
15
16 }

  运行结果如下:

  

二、访问者模式的应用

  1. 何时使用

  • 需要对一个对象结构中的对象进行很多不同的并且不相关的操作,而需要避免让这些操作“污染”这些对象的类时

  2. 方法

  • 在被访问的类里面添加一个对外提供接待访问者的接口

  3. 优点

  • 符合单一职责原则
  • 优秀的扩展性
  • 灵活性非常高

  4. 缺点

  • 具体元素对访问者公布细节,也就是说访问者关注了其他类的内部细节,这是迪米特法则所不建议的
  • 具体元素变更比较困难
  • 违背了依赖倒转原则。访问者依赖的是具体元素,而不是抽象元素

  5. 使用场景

  • 一个对象结构包含很多类对象,它们有不同的接口,而你想对这些对象实施一些依赖与其具体类的操作,也就是用迭代器模式已经不能胜任的情景
  • 需要对一个对结构中的对象进行很多不同并且不相关的操作,而你想避免让这些操作“污染”这些对象

  6. 目的

  • 把处理从数据结构分离出来

  7. 应用实例

  • 人类只分为男人和女人,这个性别分类是稳定的,可以在状态类中,增加“男人反应”和“女人反应”两个方法,方法个数是稳定的,不会很容易发生变化
  • 你在朋友家做客,你是访问者,朋友接受你的访问,你通过朋友的描述,然后对朋友的描述做出一个判断

  8. 注意事项

  • 访问者可以对功能进行统一,可以做报表、UI、拦截器与过滤器
  • 访问者模式适用于数据结构相对稳定的系统

三、访问者模式的实现

  下面就以上述应用实例中的人类分为男人和女人这个例子来实现访问者模式。UML图如下:

  1. Action

  抽象的状态类,主要声明以下两个方法。

  这里的关键在于人只分男人和女人,这个性别的分类是稳定的,所以可以在状态类中,增加“男人反应”和“女人反应”两个方法,方法个数是稳定的,不会容易发生变化。

1 public abstract class Action {
2
3     //得到男人的结论或反应
4     public abstract void getManConclusion(Man man);
5
6     //得到女人的结论或反应
7     public abstract void getWomanConclusion(Woman woman);
8
9 }

  2. Person

  人的抽象类。只有一个“接受”的抽象方法,它是用来获得“状态”对象的。

1 public abstract class Person {
2
3     //接受
4     public abstract void accept(Action action);
5
6 }

  3. Action类的具体实现类

  这里以成功类(Success)为例,失败类(Fail)同理。

 1 public class Success extends Action {
 2
 3     @Override
 4     public void getManConclusion(Man man) {
 5         System.out.println("男人成功...");
 6     }
 7
 8     @Override
 9     public void getWomanConclusion(Woman woman) {
10         System.out.println("女人成功...");
11     }
12
13 }

  4. Person类的具体实现类

  这里以男人类(Man)为例,女人类(Woman)同理。

  这里用到了双分派,即首先在客户程序中将具体状态作为参数传递给Man类完成了一次分派,然后Man类调用作为参数的“具体方法”中的方法getManConclusion(),同时将自己(this)作为参数传递进去,这便完成了第二次分派。accept方法就是一个双分派操作,它得到执行的操作不仅决定于Action类的具体状态,还决定于它访问的Person的类别。

1 public class Man extends Person {
2
3     @Override
4     public void accept(Action action) {
5         action.getManConclusion(this);
6     }
7
8 }

  5. 结构对象

 1 public class ObjectStructure {
 2
 3     private List<Person> elements = new LinkedList<>();
 4
 5     //增加
 6     public void attach(Person person) {
 7         elements.add(person);
 8     }
 9
10     //移除
11     public void detach(Person person) {
12         elements.remove(person);
13     }
14
15     //查看显示
16     public void display(Action action) {
17         for (Person person : elements) {
18             person.accept(action);
19         }
20     }
21
22 }

  6. Client客户端

 1 public class Client {
 2
 3     public static void main(String[] args) {
 4         ObjectStructure objectStructure = new ObjectStructure();
 5
 6         objectStructure.attach(new Man());
 7         objectStructure.attach(new Woman());
 8
 9         //成功
10         Success success = new Success();
11         objectStructure.display(success);
12
13         //失败
14         Failing failing = new Failing();
15         objectStructure.display(failing);
16     }
17
18 }

  运行结果如下:

  

四、双分派

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

  以上述实例为例,假设我们要添加一个Marray的状态类来考察Man类和Woman类的反应,由于使用了双分派,只需增加一个Action子类即可在客户端调用来查看,不需要改动任何其他类的代码。

  而单分派语言处理一个操作是根据请求者的名称和接收到的参数决定的,在Java中有静态绑定和动态绑定之说,它的实现是依据重载和重写实现的。值得一提的是,Java是一个支持双分派的单分派语言。

  源码地址:https://gitee.com/adamjiangwh/GoF

原文地址:https://www.cnblogs.com/adamjwh/p/10968634.html

时间: 2024-10-12 16:23:00

简说设计模式——访问者模式的相关文章

.NET设计模式访问者模式

一.访问者模式的定义: 表示一个作用于某对象结构中的各元素的操作.它使你可以在不改变各元素类的前提下定义作用于这些元素的新操作. 二.访问者模式的结构和角色: 1.Visitor 抽象访问者角色,为该对象结构中具体元素角色声明一个访问操作接口.该操作接口的名字和参数标识了发送访问请求给具体访问者的具体元素角色,这样访问者就可以通过该元素角色的特定接口直接访问它.2.ConcreteVisitor.具体访问者角色,实现Visitor声明的接口.3.Element 定义一个接受访问操作(accept

[我的设计模式] 访问者模式

访问者模式(Visitor Pattern)是GoF提出的23种设计模式中的一种,属于行为模式. 定义(源于GoF<Design Pattern>):表示一个作用于某对象结构中的各元素的操作.它使你可以在不改变各元素类的前提下定义作用于这些元素的新操作. 从定义可以看出结构对象是使用访问者模式必备条件,而且这个结构对象必须存在遍历自身各个对象的方法. 应用场景 用户访问博客.[我的设计模式] 访问者模式

【C#设计模式-访问者模式】

一.访问者模式的定义: 表示一个作用于某对象结构中的各元素的操作.它使你可以在不改变各元素类的前提下定义作用于这些元素的新操作. 二.访问者模式的结构和角色: 1.Visitor 抽象访问者角色,为该对象结构中具体元素角色声明一个访问操作接口.该操作接口的名字和参数标识了发送访问请求给具体访问者的具体元素角色,这样访问者就可以通过该元素角色的特定接口直接访问它. 2.ConcreteVisitor.具体访问者角色,实现Visitor声明的接口. 3.Element 定义一个接受访问操作(acce

最复杂的设计模式---访问者模式

今天,我们一起来探讨最后一个.最复杂的设计模式 访问者模式:它表示一个作用于某个对象结构中的各个元素的操作.它使你可以在不改变个元素的类前提下,定义作用于这些元素的新操作.这个地方要注意:1.需要有个对象结构类用于存储循环遍历的方法的方式:2.访问者模式是对元素的操作:3.可以任意添加对这些元素的新操作.确实有点复杂,我们先看看其结构图: 代码部分: Visitor类抽象声明了对各种类型元素的访问方法: abstract class Visitor { public abstract void

C#设计模式-访问者模式

一.访问者模式介绍 1.1 访问者模式的定义 表示一个作用于某对象结构中的各个元素的操作.它使你可以在不改变各个元素的类的前提下定义作用于这些元素的新操作. 1.2 访问者模式的结构图 具体的访问者模式结构图如下所示. 这里需要明确一点:访问者模式中具体访问者的数目和具体节点的数目没有任何关系.从访问者的结构图可以看出,访问者模式涉及以下几类角色. 抽象访问者角色(Vistor):声明一个活多个访问操作,使得所有具体访问者必须实现的接口. 具体访问者角色(ConcreteVistor):实现抽象

小菜学设计模式——访问者模式

背景 最后一个设计模式,也是<大话设计模式>公认最难的设计模式,当然不是理解上困难,我觉得应该是使用上困难,这个设计模式虽然具有非常良好的扩展能力,但却需要类的结构早早定义清晰,试想,需求时刻变化,你的类可以稳定吗? 1.使用意图 容易扩展,满足开发封闭原则 2.生活实例 男人和女人的状态,把ConcreteElmentA看成男人,把ConcreteElementB看成女人,那么,所有的Visitor实例就是成功状态.失败状态.结婚状态.升职状态等.把这些状态放在客户端的集合中维护,一旦需要,

PHP设计模式——访问者模式

声明:本系列博客参考资料<大话设计模式>,作者程杰. 访问者模式表示一个作用于某对象结构中的各元素的操作.它使你可以在不改变各元素类的前提下定义作用于这些元素的新操作. UML类图: 角色: 1.抽象访问者(State):为该对象结构中具体元素角色声明一个访问操作接口.该操作接口的名字和参数标识了发送访问请求给具体访问者的具体元素角色,这样访问者就可以通过该元素角色的特定接口直接访问它. 2.具体访问者(Success):实现访问者声明的接口. 3.抽象元素(Person):定义一个接受访问操

深入浅出设计模式——访问者模式(Visitor Pattern)

模式动机 对于系统中的某些对象,它们存储在同一个集合中,且具有不同的类型,而且对于该集合中的对象,可以接受一类称为访问者的对象来访问,而且不同的访问者其访问方式有所不同,访问者模式为解决这类问题而诞生.在实际使用时,对同一集合对象的操作并不是唯一的,对相同的元素对象可能存在多种不同的操作方式.而且这些操作方式并不稳定,可能还需要增加新的操作,以满足新的业务需求.此时,访问者模式就是一个值得考虑的解决方案.访问者模式的目的是封装一些施加于某种数据结构元素之上的操作,一旦这些操作需要修改的话,接受这

iOS设计模式---访问者模式

一个简单的Car模型,含有1台Engine.4个Wheel,使用访问者模式添加对Car的升级与维修操作. 定义Engine类: // //  NimoEngine.h //  VisitorDemo // //  Created by fu zheng on 15/8/12. //  Copyright (c) 2015年 FuZheng. All rights reserved. // #import <Foundation/Foundation.h> #import "NimoC