`

访问者模式 Visitor(转)

 
阅读更多
一、引子

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

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

  那么,访问者模式也许是你很好的选择。

  二、定义与结构

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

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

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

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

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

  3) 元素角色(Element):定义一个Accept操作,它以一个访问者为参数。

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

  5) 对象结构角色(Object Structure):这是使用访问者模式必备的角色。它要具备以下特征:能枚举它的元素;可以提供一个高层的接口以允许该访问者访问它的元素;可以是一个复合(组合模式)或是一个集合,如一个列表或一个无序集合。

  来张类图就能更加清晰的看清访问者模式的结构了。

  那么像引言中假想的。我们应该做些什么才能让访问者模式跑起来呢?首先我们要在原有的类层次结构中添加accept方法。然后将这个类层次中的类放到一个对象结构中去。这样再去创建访问者角色……

  三、举例

  本人阅历实在可怜,没能找到访问者模式在实际应用中的例子。只好借《Thinking in Patterns with java》中的教学代码一用。我稍微做了下修改。

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

  你是否能很好的理解呢?

  六、总结

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

 

 

扩展阅读:

在好久之前看过一篇文章,关于如何改造访问者模式,有点感触。在论坛上比较少关于访问者模式的帖子,我把自己的想法写下来,一来与大家分享一下,二来抛砖引玉,希望大家能提出关于改造访问者模式的更好的意见:)
原文在http://rjx2008.iteye.com/blog/340083写在blog没什么人看,没办法和更多的人一起讨论。

这里非结合反射(Reflection)去解决问题,而是采用另外一种的思维方式去改造访问者模式,相对于反射,我更偏向于这种。用反射改造访问者模式将在http://rjx2008.iteye.com/blog/345272http://rjx2008.iteye.com/blog/345369中有提及过。

这里引用《大话设计模式》中男人与女人原例子。
既定访问者模式的代码:
具体元素的接口与实现类

Java代码 复制代码 收藏代码
  1. public interface Person {   
  2.       void accept(Visitor visitor);   
  3. }   
  4.   
  5.   
  6. public class Woman implements Person{   
  7.   
  8.     public void accept(Visitor visitor) {   
  9.           visitor.visit(this);   
  10.     }   
  11. }   
  12.   
  13.   
  14. public class Man implements Person{   
  15.   
  16.     public void accept(Visitor visitor) {   
  17.         visitor.visit(this);   
  18.     }   
  19. }  
public interface Person {
      void accept(Visitor visitor);
}


public class Woman implements Person{

	public void accept(Visitor visitor) {
          visitor.visit(this);
	}
}


public class Man implements Person{

	public void accept(Visitor visitor) {
		visitor.visit(this);
	}
}



访问者接口与实现类,分别代表男人与女人在不同的状态下的表现

Java代码 复制代码 收藏代码
  1. public interface Visitor {   
  2.       public void visit(Man man);   
  3.       public void visit(Woman girl);   
  4. }   
  5.   
  6. //成功时Man与Woman的不同表现   
  7. public class Success implements Visitor{   
  8.   
  9.     public void visit(Man man) {   
  10.         System.out.println("当男人成功时,背后多半有一个伟大的女人");   
  11.     }   
  12.   
  13.   
  14.     public void visit(Woman woman) {   
  15.         System.out.println("当女人成功时,背后大多有一个不成功的男人");   
  16.     }   
  17. }   
  18.   
  19. //恋爱时Man与Woman的不同表现   
  20. public class Love implements Visitor{   
  21.   
  22.     public void visit(Man man) {   
  23.         System.out.println("当男人恋爱时,凡事不懂也装懂");   
  24.     }   
  25.   
  26.   
  27.     public void visit(Woman girl) {   
  28.         System.out.println("当女人恋爱时,遇事懂也装不懂");   
  29.     }   
  30. }  
public interface Visitor {
      public void visit(Man man);
      public void visit(Woman girl);
}

//成功时Man与Woman的不同表现
public class Success implements Visitor{

	public void visit(Man man) {
		System.out.println("当男人成功时,背后多半有一个伟大的女人");
	}


	public void visit(Woman woman) {
		System.out.println("当女人成功时,背后大多有一个不成功的男人");
	}
}

//恋爱时Man与Woman的不同表现
public class Love implements Visitor{

	public void visit(Man man) {
		System.out.println("当男人恋爱时,凡事不懂也装懂");
	}


	public void visit(Woman girl) {
		System.out.println("当女人恋爱时,遇事懂也装不懂");
	}
}



ObjectStructure与客户端测试代码

Java代码 复制代码 收藏代码
  1. import java.util.*;   
  2.   
  3. public class ObjectStructure {   
  4.     private List<Person> elements = new ArrayList<Person>();   
  5.   
  6.     public void attach(Person element){   
  7.         elements.add(element);   
  8.     }   
  9.        
  10.     public void detach(Person element){   
  11.         elements.remove(elements);   
  12.     }   
  13.        
  14.     //遍历各种具体元素并执行他们的accept方法   
  15.     public void display(Visitor visitor){   
  16.         for(Person p:elements){   
  17.             p.accept(visitor);   
  18.         }   
  19.     }   
  20. }   
  21.   
  22.   
  23. public class Client {   
  24.       public static void main(String[] args) {   
  25.         ObjectStructure o = new ObjectStructure();  //依赖于ObjectStructure   
  26.         //实例化具体元素   
  27.         o.attach(new Man());     
  28.         o.attach(new Woman());   
  29.            
  30.         //当成功时不同元素的不同反映   
  31.         Visitor success = new Success();           //依赖于抽象的Visitor接口   
  32.         o.display(success);   
  33.            
  34.         //当恋爱时的不同反映   
  35.         Visitor amativeness = new Love();          //依赖于抽象的Visitor接口   
  36.         o.display(amativeness);   
  37.            
  38.     }   
  39. }  
import java.util.*;

public class ObjectStructure {
    private List<Person> elements = new ArrayList<Person>();

    public void attach(Person element){
    	elements.add(element);
    }
    
    public void detach(Person element){
    	elements.remove(elements);
    }
    
    //遍历各种具体元素并执行他们的accept方法
    public void display(Visitor visitor){
    	for(Person p:elements){
    		p.accept(visitor);
    	}
    }
}


public class Client {
      public static void main(String[] args) {
		ObjectStructure o = new ObjectStructure();  //依赖于ObjectStructure
		//实例化具体元素
		o.attach(new Man());  
		o.attach(new Woman());
		
		//当成功时不同元素的不同反映
		Visitor success = new Success();           //依赖于抽象的Visitor接口
		o.display(success);
		
		//当恋爱时的不同反映
		Visitor amativeness = new Love();          //依赖于抽象的Visitor接口
		o.display(amativeness);
		
	}
}



需求的变化

假设现在需求要扩展数据结构,增加一种具体元素,男与女之外的一种不明物体,我们暂时把它称为“怪兽”,在既有访问者模式的架构下,应该怎样?首先增加一个Bruce类,实现Person接口。最麻烦的是要修改访问者接口及其所有具体访问者!

既定访问者模式的类图:




        因为Visit方法中没有包含访问Bruce对象的行为,因此我们被迫要去手工更改Visitor(包括抽象的,具体的),在其中添加有关Bruce对象的行为,这严重违反了“开放-封闭”原则。究其原因在于目前的结构下,被访问对象与访问对象互相依赖,自然不利于分离变化,必须去掉一层依赖关系。

我们尝试把Visitor对Person(元素)的依赖关系去掉,抽象出对应每个具体元素的ElementVisitor接口-->ManVisitor,WomanVisitor,然后把Visitor对Person的依赖关系转移到ManVisitor与WomanVisitor身上。
改造后访问者模式的类图:






现在Visitor接口已经没有任何抽象方法,只是一个空接口,每一个具体元素对应有一个ElementVisitor接口,每一个元素对应的ElementVisitor接口有访问该元素的visit(),相当把原来在Visitor接口中声明工作,交由各个具体ElementVisitor接口完成。


经过改造后的代码:
原Visitor接口

Java代码 复制代码 收藏代码
  1. public interface Visitor {   
  2.       //退化到没有任何抽象方法   
  3. }  
public interface Visitor {
      //退化到没有任何抽象方法
}



新增加ManVisitor,WomanVisitor接口

Java代码 复制代码 收藏代码
  1. public interface ManVisitor {   
  2.       public void visit(Man man);   
  3. }   
  4.   
  5. public interface WomanVisitor {   
  6.       public void visit(Woman w);   
  7. }  
public interface ManVisitor {
      public void visit(Man man);
}

public interface WomanVisitor {
      public void visit(Woman w);
}



具体Visitor实现类现在同时实现3个接口

Java代码 复制代码 收藏代码
  1. //由实现Visitor接口扩展成实现Visitor,WomanVisitor,ManVisitor三个接口   
  2. public class Success implements Visitor,WomanVisitor,ManVisitor{   
  3.   
  4.     public void visit(Man man) {   
  5.         System.out.println("当男人成功时,背后多半有一个伟大的女人");   
  6.     }   
  7.   
  8.     public void visit(Woman girl) {   
  9.         System.out.println("当女人成功时,背后大多有一个不成功的男人");   
  10.     }   
  11. }   
  12.   
  13.   
  14. //由实现Visitor接口扩展成实现Visitor,WomanVisitor,ManVisitor三个接口   
  15. public class Love implements Visitor,WomanVisitor,ManVisitor{   
  16.   
  17.     public void visit(Man man) {   
  18.         System.out.println("当男人恋爱时,凡事不懂也装懂");   
  19.     }   
  20.   
  21.     public void visit(Woman girl) {   
  22.         System.out.println("当女人恋爱时,遇事懂也装不懂");   
  23.     }   
  24. }  
//由实现Visitor接口扩展成实现Visitor,WomanVisitor,ManVisitor三个接口
public class Success implements Visitor,WomanVisitor,ManVisitor{

	public void visit(Man man) {
		System.out.println("当男人成功时,背后多半有一个伟大的女人");
	}

	public void visit(Woman girl) {
		System.out.println("当女人成功时,背后大多有一个不成功的男人");
	}
}


//由实现Visitor接口扩展成实现Visitor,WomanVisitor,ManVisitor三个接口
public class Love implements Visitor,WomanVisitor,ManVisitor{

	public void visit(Man man) {
		System.out.println("当男人恋爱时,凡事不懂也装懂");
	}

	public void visit(Woman girl) {
		System.out.println("当女人恋爱时,遇事懂也装不懂");
	}
}



Person接口没有变化,依旧只依赖于Visitor接口

Java代码 复制代码 收藏代码
  1. public interface Person {   
  2.       void accept(Visitor visitor);   
  3. }  
public interface Person {
      void accept(Visitor visitor);
}



改造后的具体元素类Man与Woman

Java代码 复制代码 收藏代码
  1. public class Man implements Person {   
  2.   
  3.     // 先对visitor进行类型转换,再执行visit方法,因为Visitor接口已经没有声明任何抽象方法了   
  4.     public void accept(Visitor visitor) {   
  5.         if (visitor instanceof ManVisitor) {   
  6.             ManVisitor mv = (ManVisitor) visitor;   
  7.             mv.visit(this);   
  8.         }   
  9.     }   
  10. }   
  11.   
  12.   
  13. public class Woman implements Person {   
  14.   
  15.     // 先对visitor进行类型转换,再执行visit方法,因为Visitor接口已经没有声明任何抽象方法了   
  16.     public void accept(Visitor visitor) {   
  17.         if (visitor instanceof WomanVisitor) {   
  18.             WomanVisitor wv = (WomanVisitor) visitor;   
  19.             wv.visit(this);   
  20.         }   
  21.     }   
  22. }  
public class Man implements Person {

	// 先对visitor进行类型转换,再执行visit方法,因为Visitor接口已经没有声明任何抽象方法了
	public void accept(Visitor visitor) {
		if (visitor instanceof ManVisitor) {
			ManVisitor mv = (ManVisitor) visitor;
			mv.visit(this);
		}
	}
}


public class Woman implements Person {

	// 先对visitor进行类型转换,再执行visit方法,因为Visitor接口已经没有声明任何抽象方法了
	public void accept(Visitor visitor) {
		if (visitor instanceof WomanVisitor) {
			WomanVisitor wv = (WomanVisitor) visitor;
			wv.visit(this);
		}
	}
}



ObjectStructure与客户端测试代码没有变化

Java代码 复制代码 收藏代码
  1. import java.util.*;   
  2.   
  3. public class ObjectStructure {   
  4.     private List<Person> elements = new ArrayList<Person>();   
  5.   
  6.     public void attach(Person element){   
  7.         elements.add(element);   
  8.     }   
  9.        
  10.     public void detach(Person element){   
  11.         elements.remove(elements);   
  12.     }   
  13.        
  14.     //遍历各种具体元素并执行他们的accept方法   
  15.     public void display(Visitor visitor){   
  16.         for(Person p:elements){   
  17.             p.accept(visitor);   
  18.         }   
  19.     }   
  20. }   
  21.   
  22.   
  23. public class Client {   
  24.       public static void main(String[] args) {   
  25.         ObjectStructure o = new ObjectStructure();  //依赖于ObjectStructure   
  26.         //实例化具体元素   
  27.         o.attach(new Man());     
  28.         o.attach(new Woman());   
  29.            
  30.         //当成功时不同元素的不同反映   
  31.         Visitor success = new Success();           //依赖于抽象的Visitor接口   
  32.         o.display(success);   
  33.            
  34.         //当恋爱时的不同反映   
  35.         Visitor amativeness = new Love();          //依赖于抽象的Visitor接口   
  36.         o.display(amativeness);        
  37.     }   
  38. }  
import java.util.*;

public class ObjectStructure {
    private List<Person> elements = new ArrayList<Person>();

    public void attach(Person element){
    	elements.add(element);
    }
    
    public void detach(Person element){
    	elements.remove(elements);
    }
    
    //遍历各种具体元素并执行他们的accept方法
    public void display(Visitor visitor){
    	for(Person p:elements){
    		p.accept(visitor);
    	}
    }
}


public class Client {
      public static void main(String[] args) {
		ObjectStructure o = new ObjectStructure();  //依赖于ObjectStructure
		//实例化具体元素
		o.attach(new Man());  
		o.attach(new Woman());
		
		//当成功时不同元素的不同反映
		Visitor success = new Success();           //依赖于抽象的Visitor接口
		o.display(success);
		
		//当恋爱时的不同反映
		Visitor amativeness = new Love();          //依赖于抽象的Visitor接口
		o.display(amativeness);		
	}
}



至此改造完毕!我们执行客户端测试代码,结果显示:
当男人成功时,背后多半有一个伟大的女人
当女人成功时,背后大多有一个不成功的男人
当男人恋爱时,凡事不懂也装懂
当女人恋爱时,遇事懂也装不懂

此时,客户端仍然只依赖于Visitor空接口与ObjectStructure类。可能一开始大家会认为空接口没有什么用,现在就能体现出他的威力了,使客户端与具体Visitor的高度解耦!也正是这种思维的核心
在Java API中也有类似的应用,这种空接口被称为标识接口。比如java.io.Serializable与java.rmi.Remote等,标识接口里没有任何方法和属性,标识不对实现接口不对实现它的类有任何语义上的要求,它仅仅是表明实现它的类属于一种特定的类型。
上面具体访问者实现的多个接口被称为混合类型。这个概念《Java与模式》中有提及过:当一个具体类处于一个类的等级结构之中的时候,为这个具体类定义一个混合类型是可以保证基于这个类型的可插入性的关键。

=================================无敌分界线====================================

讲了这么长,现在我们测试下改造后的访问者模式
首先增加一种行为(状态),即原访问者模式的优点

增加一个具体访问者Fail,修改一下客户端测试代码

Java代码 复制代码 收藏代码
  1. public class Fail implements Visitor,ManVisitor,WomanVisitor{   
  2.   
  3.     public void visit(Man man) {   
  4.         System.out.println("当男人失败时,闷头喝酒,谁也不用劝");   
  5.     }   
  6.   
  7.     public void visit(Woman woman) {   
  8.         System.out.println("当女人失败时,眼泪汪汪,谁也劝不了");   
  9.     }   
  10. }   
  11.   
  12.   
  13.   
  14. public class Client {   
  15.       public static void main(String[] args) {   
  16.         ObjectStructure o = new ObjectStructure();  //依赖于ObjectStructure   
  17.         //实例化具体元素   
  18.         o.attach(new Man());     
  19.         o.attach(new Woman());   
  20.            
  21.         //当成功时不同元素的不同反映   
  22.         Visitor success = new Success();           //依赖于抽象的Visitor接口   
  23.         o.display(success);   
  24.         System.out.println();   
  25.            
  26.         //当恋爱时的不同反映   
  27.         Visitor amativeness = new Love();          //依赖于抽象的Visitor接口   
  28.         o.display(amativeness);        
  29.         System.out.println();   
  30.            
  31.         //新增加失败时的不同反映   
  32.         Visitor fail = new Fail();   
  33.         o.display(fail);   
  34.     }   
  35. }  
public class Fail implements Visitor,ManVisitor,WomanVisitor{

	public void visit(Man man) {
		System.out.println("当男人失败时,闷头喝酒,谁也不用劝");
	}

	public void visit(Woman woman) {
		System.out.println("当女人失败时,眼泪汪汪,谁也劝不了");
	}
}



public class Client {
      public static void main(String[] args) {
		ObjectStructure o = new ObjectStructure();  //依赖于ObjectStructure
		//实例化具体元素
		o.attach(new Man());  
		o.attach(new Woman());
		
		//当成功时不同元素的不同反映
		Visitor success = new Success();           //依赖于抽象的Visitor接口
		o.display(success);
		System.out.println();
		
		//当恋爱时的不同反映
		Visitor amativeness = new Love();          //依赖于抽象的Visitor接口
		o.display(amativeness);		
		System.out.println();
		
		//新增加失败时的不同反映
		Visitor fail = new Fail();
		o.display(fail);
	}
}



结果显示:
当男人成功时,背后多半有一个伟大的女人
当女人成功时,背后大多有一个不成功的男人

当男人恋爱时,凡事不懂也装懂
当女人恋爱时,遇事懂也装不懂

当男人失败时,闷头喝酒,谁也不用劝
当女人失败时,眼泪汪汪,谁也劝不了



增加新的行为(状态)与原来一样方便!只需要增加一个具体访问者即可!
现在我们来增加一个具体元素(正是写这篇文章的初衷)

首先增加一个具体元素Bruce

Java代码 复制代码 收藏代码
  1. public class Bruce implements Person{   
  2.        
  3.     public void accept(Visitor visitor) {          
  4.         if(visitor instanceof BruceVisitor){   
  5.             BruceVisitor bv = (BruceVisitor) visitor;   
  6.             bv.visit(this);   
  7.         }   
  8.         //这个else可写可不写   
  9.         else{   
  10.             String s = visitor.getClass().getName();   
  11.             String state = s.substring(s.lastIndexOf(".")+1,s.length());   
  12.             System.out.println("噢..原来怪兽在"+state+"的时候是没有行为的!!");   
  13.         }          
  14.     }   
  15. }   
  16.   
  17.   
  18. //按照新的思维方式增加一个对应的ElementVisitor接口   
  19. public interface BruceVisitor {   
  20.       public void visit(Bruce bruce);   
  21. }  
public class Bruce implements Person{
    
	public void accept(Visitor visitor) {		
		if(visitor instanceof BruceVisitor){
			BruceVisitor bv = (BruceVisitor) visitor;
			bv.visit(this);
		}
		//这个else可写可不写
		else{
			String s = visitor.getClass().getName();
			String state = s.substring(s.lastIndexOf(".")+1,s.length());
			System.out.println("噢..原来怪兽在"+state+"的时候是没有行为的!!");
		}		
	}
}


//按照新的思维方式增加一个对应的ElementVisitor接口
public interface BruceVisitor {
      public void visit(Bruce bruce);
}



我们让Success这个具体访问者多实现一个BruceVisitor访问者接口,和修改一下客户端代码进行测试

Java代码 复制代码 收藏代码
  1. public class Success implements Visitor,WomanVisitor,ManVisitor,BruceVisitor{   
  2.   
  3.     public void visit(Man man) {   
  4.         System.out.println("当男人成功时,背后多半有一个伟大的女人");   
  5.     }   
  6.   
  7.     public void visit(Woman girl) {   
  8.         System.out.println("当女人成功时,背后大多有一个不成功的男人");   
  9.     }   
  10.   
  11.     public void visit(Bruce bruce) {   
  12.         System.out.println("当怪兽成功时.........无语..........");   
  13.     }   
  14. }   
  15.   
  16.   
  17. public class Client {   
  18.       public static void main(String[] args) {   
  19.         ObjectStructure o = new ObjectStructure();  //依赖于ObjectStructure   
  20.   
  21.         o.attach(new Man());     
  22.         o.attach(new Woman());   
  23.         o.attach(new Bruce());      //新增一种具体元素Bruce   
  24.            
  25.         Visitor success = new Success();           //依赖于抽象的Visitor接口   
  26.         o.display(success);   
  27.         System.out.println();   
  28.            
  29.         Visitor amativeness = new Love();          //依赖于抽象的Visitor接口   
  30.         o.display(amativeness);        
  31.         System.out.println();   
  32.            
  33.         Visitor fail = new Fail();   
  34.         o.display(fail);   
  35.     }   
  36. }  
public class Success implements Visitor,WomanVisitor,ManVisitor,BruceVisitor{

	public void visit(Man man) {
		System.out.println("当男人成功时,背后多半有一个伟大的女人");
	}

	public void visit(Woman girl) {
		System.out.println("当女人成功时,背后大多有一个不成功的男人");
	}

	public void visit(Bruce bruce) {
		System.out.println("当怪兽成功时.........无语..........");
	}
}


public class Client {
      public static void main(String[] args) {
		ObjectStructure o = new ObjectStructure();  //依赖于ObjectStructure

		o.attach(new Man());  
		o.attach(new Woman());
		o.attach(new Bruce());      //新增一种具体元素Bruce
		
		Visitor success = new Success();           //依赖于抽象的Visitor接口
		o.display(success);
		System.out.println();
		
		Visitor amativeness = new Love();          //依赖于抽象的Visitor接口
		o.display(amativeness);		
		System.out.println();
		
		Visitor fail = new Fail();
		o.display(fail);
	}
}



显示结果:
当男人成功时,背后多半有一个伟大的女人
当女人成功时,背后大多有一个不成功的男人
当怪兽成功时.........无语..........

当男人恋爱时,凡事不懂也装懂
当女人恋爱时,遇事懂也装不懂
噢..原来怪兽在Love的时候是没有行为的!!

当男人失败时,闷头喝酒,谁也不用劝
当女人失败时,眼泪汪汪,谁也劝不了
噢..原来怪兽在Fail的时候是没有行为的!!


这个结果你满意吗?
虽然,这只是部分符合“开放-封闭”原则,我们不需要修改Visitor接口,但还是得去修改Success实现新的接口。但是修改具体类比修改接口的代价小得多,不需要重新编译所有访问接口和具体访问者。使我们面对新的变化也容易得多。而且这还有一个好处,就是可以让各种元素有选择地让别人访问,如上述例子,这样使访问者模式的运用起来更加灵活

 

 

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics