高性能聊天系统

  作者:板桥banq

上页

1.4.2  访问者模式定义

访问者(Visitor)模式提供了一种可重用的通用解决模式,GOF(四位作者简称)《设计模式》中的Visitor模式定义如下:

Visitor模式表示作用于某对象集合中各元素的操作。这种操作不是一种原生的,而是使用了访问者模式后才有的方法,即这些元素原本没有做好接受这种操作的准备。因此,没有提供一种专门方法用于外界统一访问,如果强行需要对这些元素进行访问,那么就需要访问者模式为这些元素再设计一个统一接口。这样,通过这个统一接口提供的统一方法,外界可以对这些元素实现统一访问了。

可以这样通俗地理解:有一堆“身份”各异对象(通常是数据状态对象),这些对象有下面的特征:被动性(数据都是被动的),需要等待外界来操作或推动。那么现在外界有访问者来准备操作这些状态类了(例如:和这些类握手),但是走到面前,双方突然傻眼,发现谁也不认识啊(含义:这些对象没有声明可以接受这个动作访问,两者完全没有联系),这些状态类可能属于不同类型的接口,怎么办?

有一种最直接的办法就是:逐个更改这些类的接口代码。但是这样带来的问题也是显而易见的,修改的代码太多反而引起系统的不稳定。

在不能修改原有接口代码的情况下,还有一种办法,增加一个新的统一接口。让这些状态类再继承一次这个新的接口,在这个新的接口中,提供了让访问者访问操作的方法(类似Accept())。

使用访问者模式可以很容易地增加新的操作行为。只要增加一个新的访问者就可以给一批对象增加一个新的操作。否则,就需要逐个修改这些对象的代码。

在什么场合可以使用访问者模式是关键,通过研究设计模式的参与者角色,可以找到相关问题的答案。访问者模式的角色参与者有5个,如下:

·       对象集合结构(Collection):类似Collection这样的对象,该集合中包含一系列对象元素,能够使用Iterator这样的语句对这些元素实现枚举。

·       被访问接口(Visitable):定义一个Accept操作,它是以一个访问者为输入参数的。在访问者模式中,被访问接口一般是在以后扩展中难以进行变化的,这是访问者模式的一个特点,一个模式总是有不变的地方,而优势则是在可以动态扩展变化的地方。

·       被访问具体实现(ConcreteElement):实现Accept具体操作。

·       访问者接口(Visitor):这是一个主要角色,访问者主要是访问上述被访问者,通过被访问者提供的特定接口来直接访问它。

·       访问者具体实现(ConcreteVisitor):这是可以动态扩展变化的部分,也是该模式魅力所在,通过增加一个访问者具体实现,就可以增加对被访问者一系列的新行为和操作。

上述模式角色中,核心角色是3个:访问者、被访问者以及对象集合。在使用访问者模式时,如何从自己的具体项目应用中定位到这3种角色是非常关键的,当然,这个模式有一个非常重要的特点,就是有一个对象集合,在Java中就是类似Collection这样的集合,如果自己的项目中有这种集合概念,并实现对集合中元素相关操作,就可以考虑是否需要使用访问者模式了。

举一个例子:Java中的Collection(包括VectorHashtable)是大家最经常使用的技术,但是Collection好像是个黑色大染缸,本来有各种鲜明类型特征的对象一旦放入后,再取出时,这些类型就消失了,那么势必要实现强迫类型转换:

Iterator iterator = collection.iterator()  

while (iterator.hasNext()) {     //枚举集合中的元素

   Object o = iterator.next();

   if (o instanceof String)    //使用instanceof实现检验判断

      System.out.println("'"+o.toString()+"'");

   else if (o instanceof Integer){

          int temp = ((Integer)o).intValue + 1;

   }else if (o instanceof  ObjectA)

      System.out.println((ObjectA(o)).getValue);

}

如果集合Collection中元素类型有很多,那么随之这种if语句判断会变得很长。从小处看:代码结构显得不够优雅、琐碎复杂;从大处看,每增加一个类型都要修改这个关键类,极有可能触动已经稳定运行的其他代码部分,危险度很高。

本例目的有两个:首先分辨出集合元素的类型,然后针对不同类型实现不同的运算方法。如果是String类型,则打印出;如果是整数型,则实现运算;如果是ObjectA,则打印出ObjectAgetValue返回值。

因为这些元素是存放在一个集合中的,有集合的概念,可以考虑是否使用访问者模式,那么在这个应用中是否还能找到其他角色呢?

这个应用是为了对这些元素逐个访问,然后分别采取相应的行为操作。既然有访问行为发生,那么存在两个基本角色:访问者和被访问者。被访问者是这些元素,而访问者可能没有清晰的实体,但是访问者的行为能够确定,那么本例可以使用访问者模式。

首先从基本角色访问者和被访问者开始设计,被访问者是Collection中的元素,他们必须有一个统一接口,用于接受访问者的访问(accept方法)。代码如下:

public interface Visitable

{

   public void accept(Visitor visitor); //用于接受访问者

}

访问者角色有可以确定的行为,如下:

public interface Visitor

{

     public visitCollection(Collection collection); //访问行为

   public void visit(StringElement stringE);

public void visit(IntegerElement integerE); 

public void visit(ObjectAElement objectAE); 

     //可以在以后扩展中增加新的访问行为

}

两个基本接口确定后,可以设计它们的具体实现。

Visitable接口的具体实现主要是集合Collection中的元素,目前本例中有几个元素类型:StringIntegerObjectA。如果是String,则打印出这个String对象,这是针对String元素的一个行为,String是一个Java基本类型,如何使这个String具备被访问的能力,那么就要创建一个包装String对象的新类,如下:

public class StringElement implements Visitable

{

private String value;

   public StringElement (String string) {

      value = string;  //通过构造方法,将String字符串包装起来

   }

   public String getValue(){

      return value;

   }

   public void accept(Visitor visitor) {

      visitor. visit(this);  //实现回调

   }

}

通过StringElement类的构造方法的参数输入,将字符串String包装进入这个类,这种通过建立新类,将原来的类包装起来的做法实际是适配器Adapter模式,在Java中经常使用。

适配器Adapter模式是在不改动原来类的代码的情况下,将这个类进行一定的包装,使之能够适合新的应用,所以Adapter模式也叫Wrapper(包装)模式。

其他元素类型也采取Adapter模式,例如ObjectAVisitable具体实现如下:

public class ObjectAElement implements Visitable

{

private ObjectA objectA;

   public ObjectAElement (ObjectA objectA) {

      objectA = objectA;  //通过构造方法,将ObjectA包装起来

   }

   public String getValue(){

      return objectA.getValue();

   }

   public void accept(Visitor visitor) {

      visitor. visit(this);  //实现回调

   }

}

访问者的具体实现是可以动态扩展的,是访问者模式使用的原因所在。正是因为只要添加一个访问者具体实现,就可以为那些被访问者实现一个统一的行为,而不必逐个修改那些被访问者。

public class ConcreteVisitor implements Visitor

{

   //在本方法中,实现了对Collection元素的访问

   public void visitCollection(Collection collection) {

      Iterator iterator = collection.iterator()

      while (iterator.hasNext()) {

         Object o = iterator.next();

         if (o instanceof Visitable)

            ((Visitable)o).accept(this);  //统一执行accpet语句

      }

}

   public void visit(StringElement stringE) {

      System.out.println("'"+string.getValue()+"'");

   }

public void visit(IntegerElement integerE){

      Integer temp = integerE.getValue(); 

int temp = temp.intValue + 1;

}

public void visit(ObjectAElement objectAE){

System.out.println(objectAE.getValue());

}

}

客户端调用上述访问者模式如下:

Visitor visitor = new ConcreteVisitor();

visitor. visitCollection(collection);

这两句实现了原始代码冗长的if语句实现的功能,而且通过访问者模式的实现,使得代码在功能上有了进一步扩展的余地,因为修改维护都集中在一个类中的某个方法,不会触及其他的功能,保证了良好的可维护性和稳定性。

 

下页