为什么要谈谈访问者设计模式呢?因为OSG整个引擎就是用访问者设计模式建立起来的,不论是遍历节点图,还是做各种实用的功能,都需要大量的用到访问者设计模式。

先谈谈访问者设计模式的定义。

1:什么是访问者模式

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

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

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

  1. 抽象访问者

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

public abstract class Visitor {

public abstract void visitConcreteElementA(ConcreteElementA concreteElementA);

public abstract void visitConcreteElementB(ConcreteElementB concreteElementB); }

  2. 具体访问者

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

public class ConcreteVisitor1 extends Visitor {

@Override public void visitConcreteElementA(ConcreteElementA concreteElementA) {

System.out.println(concreteElementA.getClass().getName() + " 被 " + this.getClass().getName() + " 访问");

}

@Override public void visitConcreteElementB(ConcreteElementB concreteElementB) {

System.out.println(concreteElementB.getClass().getName() + " 被 " + this.getClass().getName() + " 访问");

}

}

  3. 抽象元素

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

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

public abstract class Element {

public abstract void accept(Visitor visitor);

}

  4. 具体元素

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

public class ConcreteElementA extends Element {

@Override public void accept(Visitor visitor) {

visitor.visitConcreteElementA(this); }

public void operationA() { }

}

  5. 结构对象

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

public class ObjectStructure {

private List<Element> elements = new LinkedList<>();

public void attach(Element element)

{

elements.add(element);

}

public void detach(Element element) {

elements.remove(element); }

public void accept(Visitor visitor) {

for (Element element : elements) { element.accept(visitor);

} } }

  6. Client客户端

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

public class Client {

public static void main(String[] args) {

ObjectStructure objectStructure = new ObjectStructure(); 5objectStructure.attach(new ConcreteElementA());

objectStructure.attach(new ConcreteElementB());

ConcreteVisitor1 visitor1 = new ConcreteVisitor1();

ConcreteVisitor2 visitor2 = new ConcreteVisitor2();

objectStructure.accept(visitor1);

objectStructure.accept(visitor2); }

}

  运行结果如下:

二、访问者模式的应用

  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)同理。

public class Success extends Action {

@Override public void getManConclusion(Man man)

{

System.out.println("男人成功..."); }

@Override public void getWomanConclusion(Woman woman) {

System.out.println("女人成功..."); }

}

  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是一个支持双分派的单分派语言。

cacheinterceptor第二次访问没被调用_访问者设计模式在OSG中的应用相关推荐

  1. cacheinterceptor第二次访问没被调用_双分派访问者模式的前世今生

    ❝ 学了那么久的Java,你是否知道Java是属于单分派语言还是双分派语言?什么?单分派和双分派是什么意思还不知道?了解了分派机制,就能明白访问者模式的前世今生了. ❞ 访问者模式是23种设计模式当中 ...

  2. python中函数的调用_慢步python,编程中函数的概念,python中函数的声明和调用

    函数,曾经是一个很高大尚的概念.笔者是在高中数学里认识的函数,先是从y=2x+3 这条代数式开始的.y是因变量,x是自变量,y因为x取值的变化而变化. 再后来式子变成这样:f(x)=2x+3,f(x) ...

  3. python中int对象不可调用_'int'对象在python中不可调用

    我得到了这个,我期望它在打印x.withdraw()时能打印410. Kyle 12345 500 Traceback (most recent call last): File "bank ...

  4. python rpc调用_从0到1:全面理解 RPC 远程调用

    上一篇关于 WSGI 的硬核长文,不知道有多少同学,能够从头看到尾的,不管你们有没有看得很过瘾,反正我是写得很爽,总有一种将一样知识吃透了的错觉. 今天我又给自己挖坑了,打算将 rpc 远程调用的知识 ...

  5. cuda合并访问的要求_在 CUDA C / C ++ 中使用共享内存

    在 上一篇文章 中,我研究了如何将一组线程访问的全局内存合并到一个事务中,以及对齐和跨步如何影响 CUDA 各代硬件的合并.对于最新版本的 CUDA 硬件,未对齐的数据访问不是一个大问题.然而,不管 ...

  6. jsp调用controller方法_RPC调用_服务注册与发现

    RPC调用_单体架构_SOA架构 系统架构的演变 1 传统的单体架构 1.1 什么是单体架构 一个归档包(例如 war 格式或者 Jar 格式)包含了应用所有功能的应用程序,我们通常称之 为单体应用. ...

  7. m 文件 dll matlab 中调用_利用USO服务将特权文件写入武器化

    James Forshaw发现的DiagHub DLL loading技术已经非常有名了.每当你在Windows或一些第三方软件中发现SYSTEM权限的任意文件写漏洞时,你就可以用这一招来造成任意代码 ...

  8. 不应该通过类实例访问静态成员 解决方法_今天说一说php中的类与对象

    网上的文章博客这种东西已经写烂了. 我只是写写自己理解.... 首先都知道php的面向对象,什么封装,集成,多态,灵活,简单,代码简洁,重用行强,利于维护,什么什么三个方向:灵活性,重用行,扩展性. ...

  9. python嵌套类(内部类相互调用)_核心解密Python函数在(类与函数之间)和(类与类之间)互相调用...

    image.png 一.类与函数之间 首先来看一个函数间的调用 类方法: #实现类中函数之间互相调用 #下面一个学生的简单自我介绍为例子 __metaclass__=type #自由的.动态的修改/增 ...

最新文章

  1. node-sass -v 不是内部_奔驰v260领航版的缺点奔驰v级和威霆区别
  2. 看OpenCV如何在python中实现图像检测!
  3. DevOps组织IO专业人员新角色分析与技能提升
  4. CCNP精粹系列之十七--路由映射实战,博主推荐
  5. 杭州电子科技大学计算机考研复试分数线,2019杭州电子科技大学考研复试分数线通知...
  6. 小打卡软件测试,考勤打卡软件大测评,这款打卡软件你有在用吗?
  7. nihao, woshi mr jhon
  8. 要考驾照科目二了,好紧张
  9. 职场分享:职场生涯中我们经常面临的一些困惑
  10. 英语说话方式的一点儿心得
  11. 看老程序员如何用Python的SimPy库简化复杂的编程模型的
  12. 网不好,看个漫画加载半天?教你用Python批量下载网站所有漫画(附源码和视频教程)
  13. Thirteen Days 邻接表及广度优先遍历
  14. 绘画语言的要素形状b节奏c立面d色彩,环境艺术设计理论考试题目资料.doc
  15. 关于该内存不能为read要终止程序
  16. 项目总结之Basler相机的调用
  17. PS照片美化处理实例教程(2)
  18. 湖北省十堰市谷歌高清卫星地图下载
  19. 【正则表达式】——10个非常常见的正则表达式案例,轻松搞定正则表达式
  20. 【推荐系统】Graph Embedding系列之EGES: Billion-scale Commodity Embedding for E-commerce Recommend in Alibaba

热门文章

  1. wxlogin php,wxlogin.php
  2. HTML5的非主体结构元素(header、footer、hgroup、assress)
  3. 微信连接WIFI并关注公众号的方法
  4. mysql存储过程返回多个值_数据库mysql存储过程之返回多个值的方法示例
  5. AngularJS快速入门指南15:API
  6. Html5移动开发之Localstorage(本地存储)
  7. html5 图片上传 预览
  8. MongoDB非关系型数据库开发手册
  9. 生产上完成TopN统计流程
  10. RAID技术超详细讲解