`

访问者模式进阶(二)

阅读更多
       这一篇讲述如何让访问者模式(Visitor)变得更加灵活。但并非结合反射(Reflection)去解决问题,而是采用另外一种的思维方式去改造访问者模式,相对于反射,我更偏向于这种。至于用反射改造访问者模式将会在下一篇讨论。

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

既定访问者模式的类图:




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

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






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


经过改造后的代码:
原Visitor接口
public interface Visitor {
      //退化到没有任何抽象方法
}


新增加ManVisitor,WomanVisitor接口
public interface ManVisitor {
      public void visit(Man man);
}

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



具体Visitor实现类现在同时实现3个接口
//由实现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接口
public interface Person {
      void accept(Visitor visitor);
}


改造后的具体元素类Man与Woman
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与客户端测试代码没有变化
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,修改一下客户端测试代码
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
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访问者接口,和修改一下客户端代码进行测试
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实现新的接口。但是修改具体类比修改接口的代价小得多,不需要重新编译所有访问接口和具体访问者。使我们面对新的变化也容易得多。而且这还有一个好处,就是可以让各种元素有选择地让别人访问,如上述例子,这样使访问者模式的运用起来更加灵活
  • 大小: 50 KB
  • 大小: 60.2 KB
分享到:
评论
1 楼 sanpic 2015-07-23  
请问你的UML是用什么工具画的,谢谢

相关推荐

    java中级进阶高级23种设计模式详细介绍+代码详解PPT模板.pptx

    访问者模式 java中级进阶高级23种设计模式详细介绍+代码详解PPT模板全文共26页,当前为第23页。 中介者模式 java中级进阶高级23种设计模式详细介绍+代码详解PPT模板全文共26页,当前为第24页。 解释器模式 java中级...

    Java设计模式整理

    java设计模式word整理,付代码说明,时候java进阶选手,欢迎选购! 目录 1 1. 设计模式 2 1.1 创建型模式 2 1.1.1 工厂方法 2 1.1.2 抽象工厂 4 1.1.3 建造者模式 6 1.1.4 单态模式 9 ...1.3.11 访问者模式 43

    GO语言进阶.docx

    9-4_RabbitMQ工作原理和转发模式.mp4 9-5_Docker安装RabbitMQ及UI管理.mp4 9-6_编码实战_实现异步转移的MQ生产者.mp4 9-7_编码实战_实现异步转移的MQ消费者.mp4 9-8_编码实战_异步转移文件测试+小结.mp4 第10章...

    实验12 单例模式与枚举.doc

    本专栏主要为Java程序设计(基础)实验报告和Java程序设计(进阶)实验报告,基础篇有JAVA环境搭建、Java语言基础、方法和数组、面向对象基础、Java常用类、继承与接口、成员访问控制与异常、JavaFX程序设计、Java...

    实验7 成员访问控制与异常.doc

    本专栏主要为Java程序设计(基础)实验报告和Java程序设计(进阶)实验报告,基础篇有JAVA环境搭建、Java语言基础、方法和数组、面向对象基础、Java常用类、继承与接口、成员访问控制与异常、JavaFX程序设计、Java...

    基于身份认证和多模式的AES保密通信协议C++源码(含项目说明).zip

    二. 分组密码DES算法 1. DES加密 2. DES解密 3. 雪崩效应检验 三. 分组密码AES算法 1. AES加密 2. AES解密 3. 雪崩效应检验 四. RSA密码算法 1. 随机大素数生成 2. RSA加密 3. RSA解密 五. MD5密码算法 1. MD...

    实验9 Servlet.doc

    本专栏主要为Java程序设计(基础)实验报告和Java程序设计(进阶)实验报告,基础篇有JAVA环境搭建、Java语言基础、方法和数组、面向对象基础、Java常用类、继承与接口、成员访问控制与异常、JavaFX程序设计、Java...

    实验9 Java输入输出流.doc

    本专栏主要为Java程序设计(基础)实验报告和Java程序设计(进阶)实验报告,基础篇有JAVA环境搭建、Java语言基础、方法和数组、面向对象基础、Java常用类、继承与接口、成员访问控制与异常、JavaFX程序设计、Java...

    Java实验7 序列化.doc

    本专栏主要为Java程序设计(基础)实验报告和Java程序设计(进阶)实验报告,基础篇有JAVA环境搭建、Java语言基础、方法和数组、面向对象基础、Java常用类、继承与接口、成员访问控制与异常、JavaFX程序设计、Java...

    Java实验8 数据库.doc

    本专栏主要为Java程序设计(基础)实验报告和Java程序设计(进阶)实验报告,基础篇有JAVA环境搭建、Java语言基础、方法和数组、面向对象基础、Java常用类、继承与接口、成员访问控制与异常、JavaFX程序设计、Java...

    实验11 XML解析.doc

    本专栏主要为Java程序设计(基础)实验报告和Java程序设计(进阶)实验报告,基础篇有JAVA环境搭建、Java语言基础、方法和数组、面向对象基础、Java常用类、继承与接口、成员访问控制与异常、JavaFX程序设计、Java...

    实验10 JSP.doc

    本专栏主要为Java程序设计(基础)实验报告和Java程序设计(进阶)实验报告,基础篇有JAVA环境搭建、Java语言基础、方法和数组、面向对象基础、Java常用类、继承与接口、成员访问控制与异常、JavaFX程序设计、Java...

    Java实验6多线程.doc

    本专栏主要为Java程序设计(基础)实验报告和Java程序设计(进阶)实验报告,基础篇有JAVA环境搭建、Java语言基础、方法和数组、面向对象基础、Java常用类、继承与接口、成员访问控制与异常、JavaFX程序设计、Java...

    实验5 网络编程.doc

    本专栏主要为Java程序设计(基础)实验报告和Java程序设计(进阶)实验报告,基础篇有JAVA环境搭建、Java语言基础、方法和数组、面向对象基础、Java常用类、继承与接口、成员访问控制与异常、JavaFX程序设计、Java...

    Java实验2 反射.doc

    本专栏主要为Java程序设计(基础)实验报告和Java程序设计(进阶)实验报告,基础篇有JAVA环境搭建、Java语言基础、方法和数组、面向对象基础、Java常用类、继承与接口、成员访问控制与异常、JavaFX程序设计、Java...

    Java实验3泛型.doc

    本专栏主要为Java程序设计(基础)实验报告和Java程序设计(进阶)实验报告,基础篇有JAVA环境搭建、Java语言基础、方法和数组、面向对象基础、Java常用类、继承与接口、成员访问控制与异常、JavaFX程序设计、Java...

    Java实验4 注解.doc

    本专栏主要为Java程序设计(基础)实验报告和Java程序设计(进阶)实验报告,基础篇有JAVA环境搭建、Java语言基础、方法和数组、面向对象基础、Java常用类、继承与接口、成员访问控制与异常、JavaFX程序设计、Java...

    Java实验1代码.doc

    本专栏主要为Java程序设计(基础)实验报告和Java程序设计(进阶)实验报告,基础篇有JAVA环境搭建、Java语言基础、方法和数组、面向对象基础、Java常用类、继承与接口、成员访问控制与异常、JavaFX程序设计、Java...

    实验5 JAVA常用类.doc

    本专栏主要为Java程序设计(基础)实验报告和Java程序设计(进阶)实验报告,基础篇有JAVA环境搭建、Java语言基础、方法和数组、面向对象基础、Java常用类、继承与接口、成员访问控制与异常、JavaFX程序设计、Java...

    实验1 Java环境搭建.doc

    本专栏主要为Java程序设计(基础)实验报告和Java程序设计(进阶)实验报告,基础篇有JAVA环境搭建、Java语言基础、方法和数组、面向对象基础、Java常用类、继承与接口、成员访问控制与异常、JavaFX程序设计、Java...

Global site tag (gtag.js) - Google Analytics