`

访问者模式(Android中大量使用此模式)

阅读更多

 

访问者模式

 

一、引子

  对于系统中一个已经完成的类层次结构,我们已经给它提供了满足需求的接口。但是面对新增加的需求,我们应该怎么做呢?如果这是为数不多的几次变动,而且你不用为了一个需求的调整而将整个类层次结构统统地修改一遍,那么直接在原有类层次结构上修改也许是个不错的主意。

  但是往往我们遇到的却是:这样的需求变动也许会不停的发生;更重要的是需求的任何变动可能都要让你将整个类层次结构修改个底朝天。这种类似相同的操作分布在不同的类里面,不是一个好现象,我们要对这个结构重构一下了。

  那么,访问者模式也许是你很好的选择(定义一个公共的接口,然后实现它并重构需求变动)。

 

二、定义与结构

  访问者模式,顾名思义使用了这个模式后就可以在不修改已有程序结构的前提下,通过添加额外的“访问者”来完成对已有代码功能的提升。

   《设计模式》一书对于访问者模式给出的定义为:表示一个作用于某对象结构中的各元素的操作。它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作。从定义可以看出结构对象是使用访问者模式必须条件,而且这个结构对象必须存在遍历自身各个对象的方法。这便类似于java中的collection概念了。

 

以下是访问者模式的组成结构:

  1) 访问者角色(Visitor):为该对象结构中具体元素角色声明一个访问操作接口(客户端和服务器端通信的桥梁,也就是通过接口访问)。该操作接口的名字和参数标识了发送访问请求给具体访问者的具体元素角色。这样访问者就可以通过该元素角色的特定接口直接访问它。

  2) 具体访问者角色(Concrete Visitor):实现每个由访问者角色(Visitor)声明的操作(客户端)

  3) 元素角色(Element):定义一个Accept操作,它以一个访问者为参数(客户端需要接受的方法)

  4) 具体元素角色(Concrete Element):实现由元素角色提供的Accept操作。

  5) 对象结构角色(Object Structure):这是使用访问者模式必备的角色。它要具备以下特征:

(1)能枚举它的元素(注册的服务个数);可以提供一个高层的接口以允许该访问者访问它的元素(接口);可以是一 个复合(组合模式)或是一个集合,如一个列表或一个无序集合。


 

三、举例

Java 代码  收藏代码
  1. import java.util.*;  
  2. import junit.framework.*;  
  3.   
  4. //访问者角色  
  5.   
  6. interface Visitor {  
  7.  void visit(Gladiolus g);  
  8.  void visit(Runuculus r);  
  9.  void visit(Chrysanthemum c);  
  10. }  
  11.   
  12. // The Flower hierarchy cannot be changed:  
  13. //元素角色  
  14.   
  15. interface Flower {  
  16.  void accept(Visitor v);  
  17. }  
  18.   
  19. //以下三个具体元素角色  
  20.   
  21. class Gladiolus implements Flower {  
  22.  public void accept(Visitor v) { v.visit(this);}  
  23. }  
  24.   
  25. class Runuculus implements Flower {  
  26.  public void accept(Visitor v) { v.visit(this);}  
  27. }  
  28.   
  29. class Chrysanthemum implements Flower {  
  30.  public void accept(Visitor v) { v.visit(this);}  
  31. }  
  32.   
  33. // Add the ability to produce a string:  
  34. //实现的具体访问者角色  
  35.   
  36. class StringVal implements Visitor {  
  37.   String s;  
  38.  public String toString() { return s; }  
  39.  public void visit(Gladiolus g) {  
  40.    s = "Gladiolus";  
  41.   }  
  42.   
  43.  public void visit(Runuculus r) {  
  44.    s = "Runuculus";  
  45.   }  
  46.   
  47.  public void visit(Chrysanthemum c) {  
  48.    s = "Chrysanthemum";  
  49.   }  
  50. }  
  51.   
  52. // Add the ability to do "Bee" activities:  
  53. //另一个具体访问者角色  
  54.   
  55. class Bee implements Visitor {  
  56.  public void visit(Gladiolus g) {  
  57.    System.out.println("Bee and Gladiolus");  
  58.   }  
  59.   
  60.  public void visit(Runuculus r) {  
  61.    System.out.println("Bee and Runuculus");  
  62.   }  
  63.   
  64.  public void visit(Chrysanthemum c) {  
  65.    System.out.println("Bee and Chrysanthemum");  
  66.   }  
  67. }  
  68.   
  69. //这是一个对象生成器  
  70. //这不是一个完整的对象结构,这里仅仅是模拟对象结构中的元素  
  71.   
  72. class FlowerGenerator {  
  73.  private static Random rand = new Random();  
  74.  public static Flower newFlower() {  
  75.    switch(rand.nextInt(3)) {  
  76.   default:  
  77.    case 0return new Gladiolus();    
  78.    case 1return new Runuculus();  
  79.     case 2return new Chrysanthemum();  
  80.    }  
  81.  }  
  82. }  
  83.   
  84. //客户测试程序  
  85.   
  86. public class BeeAndFlowers extends TestCase {  
  87.   
  88.  /* 
  89.    在这里你能看到访问者模式执行的流程: 
  90.    首先在客户端先获得一个具体的访问者角色 
  91.    遍历对象结构 
  92.   对每一个元素调用 accept方法,将具体访问者角色传入 
  93.    这样就完成了整个过程 
  94.  */  
  95.  //对象结构角色在这里才组装上  
  96.   
  97.   List flowers = new ArrayList();  
  98.  public BeeAndFlowers() {  
  99.    for(int i = 0; i < 10; i++)  
  100.     flowers.add(FlowerGenerator.newFlower());  
  101.  }  
  102.   
  103.   Visitor sval ;  
  104.  public void test() {  
  105.    // It’s almost as if I had a function to  
  106.    // produce a Flower string representation:  
  107.    //这个地方你可以修改以便使用另外一个具体访问者角色  
  108.   
  109.    sval = new StringVal();  
  110.    Iterator it = flowers.iterator();  
  111.   while(it.hasNext()) {  
  112.     ((Flower)it.next()).accept(sval);  
  113.     System.out.println(sval);  
  114.   }  
  115.   }  
  116.   
  117.  public static void main(String args[]) {  
  118.    junit.textui.TestRunner.run(BeeAndFlowers.class);  
  119.   }  
  120.   
  121. }  
import java.util.*;
import junit.framework.*;

//访问者角色

interface Visitor {
 void visit(Gladiolus g);
 void visit(Runuculus r);
 void visit(Chrysanthemum c);
}

// The Flower hierarchy cannot be changed:
//元素角色

interface Flower {
 void accept(Visitor v);
}

//以下三个具体元素角色

class Gladiolus implements Flower {
 public void accept(Visitor v) { v.visit(this);}
}

class Runuculus implements Flower {
 public void accept(Visitor v) { v.visit(this);}
}

class Chrysanthemum implements Flower {
 public void accept(Visitor v) { v.visit(this);}
}

// Add the ability to produce a string:
//实现的具体访问者角色

class StringVal implements Visitor {
 String s;
 public String toString() { return s; }
 public void visit(Gladiolus g) {
  s = "Gladiolus";
 }

 public void visit(Runuculus r) {
  s = "Runuculus";
 }

 public void visit(Chrysanthemum c) {
  s = "Chrysanthemum";
 }
}

// Add the ability to do "Bee" activities:
//另一个具体访问者角色

class Bee implements Visitor {
 public void visit(Gladiolus g) {
  System.out.println("Bee and Gladiolus");
 }

 public void visit(Runuculus r) {
  System.out.println("Bee and Runuculus");
 }

 public void visit(Chrysanthemum c) {
  System.out.println("Bee and Chrysanthemum");
 }
}

//这是一个对象生成器
//这不是一个完整的对象结构,这里仅仅是模拟对象结构中的元素

class FlowerGenerator {
 private static Random rand = new Random();
 public static Flower newFlower() {
  switch(rand.nextInt(3)) {
  default:
   case 0: return new Gladiolus(); 
   case 1: return new Runuculus();
   case 2: return new Chrysanthemum();
  }
 }
}

//客户测试程序

public class BeeAndFlowers extends TestCase {

 /*
  在这里你能看到访问者模式执行的流程:
  首先在客户端先获得一个具体的访问者角色
  遍历对象结构
  对每一个元素调用accept方法,将具体访问者角色传入
  这样就完成了整个过程
 */
 //对象结构角色在这里才组装上

 List flowers = new ArrayList();
 public BeeAndFlowers() {
  for(int i = 0; i < 10; i++)
   flowers.add(FlowerGenerator.newFlower());
 }

 Visitor sval ;
 public void test() {
  // It’s almost as if I had a function to
  // produce a Flower string representation:
  //这个地方你可以修改以便使用另外一个具体访问者角色

  sval = new StringVal();
  Iterator it = flowers.iterator();
  while(it.hasNext()) {
   ((Flower)it.next()).accept(sval);
   System.out.println(sval);
  }
 }

 public static void main(String args[]) {
  junit.textui.TestRunner.run(BeeAndFlowers.class);
 }

}

 四、双重分派

  对了,你在上面的例子中体会到双重分派的实现了没有?

  首先在客户程序中将具体访问者模式作为参数传递给具体元素角色(加亮的地方所示)。这便完成了一次分派。

  进入具体元素角色后,具体元素角色调用作为参数的具体访问者模式中的visitor方法,同时将自己(this)作为参数传递进去。具体访问者模式再 根据参数的不同来选择方法来执行(加亮的地方所示)。这便完成了第二次分派。

  五、优缺点及适用情况

   先来看下访问者模式的使用能否避免引言中的痛苦。使用了访问者模式以后,对于原来的类层次增加新的操作,仅仅需要实现一个具体访问者角色就可以了,而不 必修改整个类层次。而且这样符合“开闭原则”的要求。而且每个具体的访问者角色都对应于一个相关操作,因此如果一个操作的需求有变,那么仅仅修改一个具体访问者角色,而不用改动整个类层次

  看来访问者模式确实能够解决我们面临的一些问题。

  而且由于访问者模式为我们的系统多提供了一层“访问者”,因此我们可以在访问者中添加一些对元素角色的额外操作。

  但是“开闭原则”的遵循总是片面的。如果系统中的类层次发生了变化,会对访问者模式产生什么样的影响呢?你必须修改访问者角色和每一个具体访问者角 色……

   看来访问者角色不适合具体元素角色经常发生变化的情况。而且访问者角色要执行与元素角色相关的操作,就必须让元素角色将自己内部属性暴露出来,而在 java中就意味着其它的对象也可以访问。这就破坏了元素角色的封装性。而且在访问者模式中,元素与访问者之间能够传递的信息有限,这往往也会限制访问者 模式的使用。

  《设计模式》一书中给出了访问者模式适用的情况:

  1) 一个对象结构包含很多类对象,它们有不同的接口,而你想对这些对象实施一些依赖于其具体类的操作。

  2) 需要对一个对象结构中的对象进行很多不同的并且不相关的操作,而你想避免让这些操作“污染”这些对象的类。Visitor使得你可以将相关的操作集中起来定义在一个类中。

  3) 当该对象结构被很多应用共享时,用Visitor模式让每个应用仅包含需要用到的操作。

  4) 定义对象结构的类很少改变,但经常需要在此结构上定义新的操作。改变对象结构类需要重定义对所有访问者的接口,这可能需要很大的代价。如果对象结构类经常 改变,那么可能还是在这些类中定义这些操作较好。

  你是否能很好的理解呢?

  六、总结

  这是一个巧妙而且复杂的模式,它的使用条件比较苛刻。当系统中存在着固定的数据结构(比如上面的类层次),而有着不同的行为,那么访问者模式也许是个不错的选择。   

分享到:
评论

相关推荐

    Android源码设计模式解析与实战PDF

    优化代码的首步、开闭原则、里氏替换原则、依赖倒置原则、接口隔离原则、迪米特原则、单例模式、Builder模式、...访问者模式、中介者模式、代理模式、组合模式、适配器模式、装饰模式、享元模式、外观模式、桥接模式...

    Android应用开发案例教程 (毋建军、徐振东、林瀚 编著) pdf

    《高等学校计算机科学与技术项目驱动案例实践规划教材:Android应用开发案例教程》应用“项目驱动(Project-Driven)”最新教学模式,通过完整的项目案例系统地介绍了使用Android技术设计与开发应用系统的理论和方法...

    Android应用开发案例教程

    , 《Android应用开发案例教程(高等学校计算机科学与技术项目驱动案例实践规划教材)》应用“项目驱动(project-driven) ”最新教学模式,通过完整的项目案例系统地介绍了使用android技术设计与开发应用系统的理论和...

    NetSupport School 课堂管理软件(简体中文版)

    NetSupport 教师助理应用程序是传统桌面教师端应用程序的延伸,可在您现有的 NetSupport 管理的教室环境中使用,为教师提供了在 ICT 教学区周围更好的移动性,助教利用这一理想工具可以帮助监督学生进度。...

    eacpay-android:eacpay手机钱包

    简单又安全EACpay钱包是开始使用Earthcoin的最佳方式。 我们简单,流线型的设计对于初学者来说很容易,但对于有经验的用户来说... 即使将eacpay钱包从App Store中删除,该应用程序仍将继续运行,允许用户随时访问其宝贵

    chrome.exe

    ·具备隐私浏览模式,可以让用户无需在本地机器上登录即可使用,这个功能与微软IE8中的Incognito类似。 ·浏览器将内置防止“网络钓鱼“及恶意软件功能。 Google曾在自己的官方博客中宣布在Beta版Chrome25 中将...

    Linux高性能服务器编程

    8.5.1 半同步半异步模式 8.5.2 领导者追随者模式 8.6 有限状态机 8.7 提高服务器性能的其他建议 8.7.1 池 8.7.2 数据复制 8.7.3 上下文切换和锁 第9章 IO复用 9.1 select系统调用 9.1.1 select API 9.1.2...

    木蚂蚁市场

    木蚂蚁已收录上万款适合国内用户使用的应用及游戏,囊括国内几大安卓资源站的数据及民间汉化组大量汉化作品,而且每天都有大 量创作者向木蚂蚁提交收录; -完全免费下载,无需注册 所有应用及游戏完全免费,直接访问...

    JAVA上百实例源码以及开源项目

     Java非对称加密源程序代码实例,本例中使用RSA加密技术,定义加密算法可用 DES,DESede,Blowfish等。  设定字符串为“张三,你好,我是李四”  产生张三的密钥对(keyPairZhang)  张三生成公钥(publicKeyZhang...

    客客出品专业威客系统KPPW v2.5 GBK.zip

    KPPW是客客团队主打的开源威客系统,同类开源建站产品的领跑者!2014年6月18日KPPW2.5正式版发布了,新版借助客客团队近4年来威客行业的开源产品和商业项目的成熟经验,对前端功能进行了深入改进。KPPW从产品规划、...

    史上最好传智播客就业班.net培训教程60G 不下会后悔

    大型互联网站由于访问量非常大,因此不是那些单纯用ASP.Net控件拖拽开发的开发人员能够开发的,也不是闭门造车能够造出来的,必须是在实际项目中经过无数访问者的使用、反馈、修改才能做出来的。搜房网、汽车之家等...

    JAVA上百实例源码以及开源项目源代码

    Java非对称加密源码实例 1个目标文件 摘要:Java源码,算法相关,非对称加密 Java非对称加密源程序代码实例,本例中使用RSA加密技术,定义加密算法可用 DES,DESede,Blowfish等。 设定字符串为“张三,你好,我是李四”...

    客客出品专业威客系统KPPW源码 v2.5

    KPPW是客客团队主打的开源威客系统,同类开源建站产品的领跑者!2014年6月18日KPPW2.5正式版发布了,新版借助客客团队近4年来威客行业的开源产品和商业项目的成熟经验,对前端功能进行了深入改进。KPPW从产品规划、...

    《程序员》杂志2012年第1期.pdf (免费积分下载)

    正如Robert Scoble在近两年前Siri被Apple收购时撰文所指出的,Siri代表了继1990年代“通过域名找页面”、2000年代“通过页面交互聚人”之后的第三代互联网交互模式:“消除网页,将API与人直接联系在一起”。...

Global site tag (gtag.js) - Google Analytics