`
阅读更多
     做了标题党=。=这一篇并没有介绍关于访问者模式的进阶,先说一下访问者模式的使用情况,这里引用大话设计模式的例子,因为讲得比较精辟。

假设有男人和女人两种元素,要分别打印出他们在不同状态时的不同表现。
用OO的思想把表现(行为)提取出来作为一个抽象方法,代码如下:

用if-else对状态进行判断
Person接口
public interface Person {
      public void action(String state);
}


Man实现类
public class Man implements Person{

	public void action(String state) {
		if(state == "success"){
			System.out.println("当男人成功时,背后多半有一个伟大的女人");
		}
		else if(state == "love"){
			System.out.println("当男人恋爱时,凡事不懂也装懂");
		}
	}   
}


Woman实现类
public class Woman implements Person{

	public void action(String state) {
		if(state == "success"){
			System.out.println("当女人成功时,背后大多有一个不成功的男人");
		}
		else if(state == "love"){
			System.out.println("当女人恋爱时,遇事懂也装不懂");
		}
	}

}


客户端测试代码

public class Client {
     public static void main(String[] args) {
		Person man = new Man();
		Person woman = new Woman();
		man.action("success");
		woman.action("success");
		
		man.action("love");
		woman.action("love");
	}
}


结果显示:
当男人成功时,背后多半有一个伟大的女人
当女人成功时,背后大多有一个不成功的男人
当男人恋爱时,凡事不懂也装懂
当女人恋爱时,遇事懂也装不懂


当需求发生变化时,要增加一种失败状态时,增加男人女人的不同表现,这时就要修改Man类与Woman类的if else,违反了ocp原则(对增加开放-对修改封闭)。而且随着需求的增加,Man类与Woman类的if,else越来越臃肿,需要取消时,又要去修改if,else,既不方便,又容易出错。
这时候访问者模式可以派上用场了。
请看下面经修改后的类图:




使用访问者模式

把状态抽象出来成为一个接口(访问者),不同的状态就作为状态的不同实现类(不同的访问者)。
状态的接口(访问者接口)
public interface Visitor {
      public void visit(Man man);
      public void visit(Woman woman);
}


具体访问者实现类(分别代表不同的状态)
//成功时Man与Woman的不同表现
public class Success implements Visitor{

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

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


public class Love implements Visitor{

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

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


按照类图改造一下人的接口与实现类
public interface Person {
      void accept(Visitor visitor);
}


public class Man implements Person{

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


public class Woman implements Person{

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

这时Man与Woman变得轻盈多了,不再需要写一大段的if,else,只需要按不同的状态,传入不同的访问者,执行访问者的方法就OK了。


为了更好地实现客户类与具体元素的解耦,加入一个ObjectStructure类。有了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);
	}
}


这时客户端只依赖于ObjectStructure类与Visitor接口,实现了高度的解耦,具体Visitor实现类的实例化与具体元素(Man,Woman)的创建可以通过工厂模式甚至配合properties/xml配置文件创建,无需客户端关注。而Man与Gril的创建的代码也可以放在其他地方,客户端无需知道,如可以放到ObjectStructure的构造函数中完成
	public ObjectStructure(){
		attach(new Man());  
		attach(new Woman());
	}

在实例块{ }中完成实例化也可以。这时如果要增加一种状态的不同操作,只需要增加一个具体访问者,无需要修改具体元素类Man与Woman。代码如下,
public class Fail implements Visitor{

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

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


修改一下客户端测试代码就OK:
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 fail = new Fail();
		o.display(fail);
	}
}


结果显示:
当男人成功时,背后多半有一个伟大的女人
当女人成功时,背后大多有一个不成功的男人
当男人恋爱时,凡事不懂也装懂
当女人恋爱时,遇事懂也装不懂
当男人失败时,闷头喝酒,谁也不用劝
当女人失败时,眼泪汪汪,谁也劝不了



现在来让我们看看访问者模式的定义与类图:
访问者模式定义:表示一个作用于某个对象结构中的各元素的操作。它使可以在不改变各元素的类的前提下定义作用于这些元素的新操作。

在我看来可以把访问者模式想象成状态模式的或者策略模式的扩展版(说策略模式会更贴切,策略与状态是有区别的),都是实现对象在不同情况下的不同表现,只是状态与策略主要是针对一种对象,而访问者是针对多种对象。





访问者模式的特点:
1)优点:使用了访问者模式以后,对于原来的类层次增加新的操作,仅仅需要实现一个具体访问者角色就可以了,而不必修改整个类层次,使得类层次结构的代码臃肿难以维护。而且这样符合“开闭原则”的要求。而且每个具体的访问者角色都对应于一个相关操作,因此如果一个操作的需求有变,那么仅仅修改一个具体访问者角色,而不用改动整个类层次。

2)访问者模式的双重分派技术
(1)将具体访问者作为参数传递给具体元素角色
(2)进入具体元素角色后,具体元素角色调用者作为参数的具体访问者的visit方法,同时将自己(this)作为参数传递进行。具体访问者再根据参数的不同来执行相应的方法

3)前提:开闭原则”的遵循总是片面的。如果系统中的类层次发生了变化,会对访问者模式产生什么样的影响呢?你必须修改访问者接口和每一个具体访问者。因此4人组曾经提出,访问者模式适用于数据结构相对稳定的系统

4)适用情况:访问者模式的目的是要把处理从数据结构分离出来。很多系统可以按照算法和数据结构分开,如果这样的系统有比较稳定的数据结构,又有易于变化的算法的话,使用访问者模式是比较合适的,因为访问者模式似得算法操作的增加变得容易。反之,如果这样的系统的数据结构对象易于变化,经常要有新的数据对象增加进来,就不适合使用访问者模式。

5)思考:如何让访问者模式变得更加灵活?
Java的Reflect技术解决了上述问题,因此结合reflect反射机制,可以使得访问者模式适用范围更广了。想了解具体详情,请看下一篇文章访问者模式进阶(二)
  • 大小: 23.7 KB
  • 大小: 24.5 KB
分享到:
评论
2 楼 sanpic 2015-07-22  
  不错
1 楼 zhangleipd 2013-07-09  

相关推荐

    java进阶必看书籍

    包括创建型模式(如单例模式、工厂方法模式、抽象工厂模式、建造者模式和原型模式)、结构型模式(如适配器模式、装饰器模式、代理模式、桥接...访问者模式、责任链模式、命令模式、备忘录模式、状态模式和解释器模式)...

    LUA进阶源代码欣赏

    源代码中可能包含一些LUA特有的设计模式,如单例模式、观察者模式等,通过这些模式,开发者可以编写出更加优雅和可维护的代码。 总的来说,"LUA进阶源代码欣赏"是一个全面了解和提升LUA编程技巧的宝贵资源。通过...

    C++进阶(语法篇)

    设计模式则是面向对象软件工程中用于解决特定问题的一般性模板,它包含了多个经典模式如工厂模式、单例模式、策略模式、观察者模式、适配器模式等,这些模式在C++编程实践中经常被应用。 通过本文对C++进阶语法的...

    Java进阶之设计模式内含源码以及说明书可以自己运行复现.zip

    行为型设计模式,观察者模式定义了对象之间的一对多依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都会得到通知并自动更新。在Java中,java.util.Observable和java.util.Observer接口就是实现这一模式...

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

    21. 访问者模式:表示一个作用于某对象结构中的各元素的操作,它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作。 22. 中介者模式:用一个中介对象来封装一系列的对象交互,中介者使各对象不需要...

    stm32进阶实验应用

    STM32是一款基于ARM Cortex-M内核的微控制器,由意法半导体公司(STMicroelectronics)生产,广泛应用于...在"进阶应用"这个文件中,可能包含了相应的代码示例、实验步骤和解释,帮助学习者一步步探索STM32的高级特性。

    进阶篇.pdf

    行为型设计模式如模板方法模式、命令模式、迭代器模式、观察者模式、中介者模式、备忘录模式、解释器模式、状态模式、策略模式、责任链模式、访问者模式,主要处理对象间的交互和行为。 单例模式有多种实现方式,...

    重塑java基础高级进阶资源分享

    - **观察者模式**:当对象的状态改变时,所有依赖于它的对象都会得到通知并被自动更新。 #### 5. 高级框架和技术 - **Spring框架**:提供了一种轻量级的DI容器,简化了Java EE应用程序开发。 - **Hibernate**:是一...

    Istio 服务网格进阶实战.pdf

    总结起来,Istio服务网格进阶实战主要涵盖了Istio的概念原理、服务网格的架构与实现模式、数据平面与控制平面的配置与管理、流量管理基础概念以及安全、监控和多集群部署等多个方面。随着微服务架构在云计算时代的...

    2011.06 - Java语言程序设计-进阶篇(原书第8版

    进阶篇可能会涉及一些Java中常用的模式,例如单例模式、工厂模式、观察者模式等。 10. 开发工具和环境:最后,进阶篇也可能简要介绍Java开发中常用的一些工具和环境,如集成开发环境(IDEs)、版本控制(如Git)...

    设计模式源码

    包括责任链模式(Chain of Responsibility)、命令模式(Command)、解释器模式(Interpreter)、迭代器模式(Iterator)、中介者模式(Mediator)...Strategy)、模板方法模式(Template Method)和访问者模式(Visitor)...

    Csharp设计模式电子版--一本很好C#设计模式

    3. 行为型模式:命令模式(Command)、解释器模式(Interpreter)、迭代器模式(Iterator)、访问者模式(Visitor)、备忘录模式(Memento)、观察者模式(Observer)、状态模式(State)、策略模式(Strategy)、...

    perl语言进阶

    《Perl语言进阶》这本书不仅是一本关于Perl语言的教程,更是一本深入探索现代计算技术领域的指南。通过对上述章节的细致学习,读者不仅能掌握Perl的高级特性和技术细节,还能了解到如何将这些知识应用于解决实际问题...

    C#23种设计模式【完整】.pdf

    20. 访问者模式(Visitor Pattern):表示一个作用于某对象结构中的各元素的操作,使得可以在不改变该对象结构的前提下定义新的操作。 21. 状态模式(State Pattern):允许一个对象在其内部状态改变时改变其行为,...

    Java进阶编程(经典网帖合集)

    10. **设计模式**:学习和掌握常见的设计模式,如单例、工厂、观察者、装饰者、策略、代理等,能够提升代码的可复用性和可维护性。 11. **注解(Annotation)**:注解是Java提供的一种元数据,用于提供编译时和运行...

    C++二十三种设计模式.pdf

    17. **访问者模式**:访问者模式表示一个作用于某对象结构中的各元素的操作,它可以在不改变各元素的类的前提下定义作用于这些元素的新操作。 18. **状态模式**:状态模式允许对象在其内部状态改变时改变其行为,...

    [中文]Head-First设计模式.pdf

    16. 访问者模式:表示一个作用于某对象结构中的各元素的操作。它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作。 17. 结构型模式:如代理、适配器、桥接、组合、装饰、外观和享元模式,它们关注的...

    前端全栈进阶 Nextjs打造跨框架SaaS应用

    ### 前端全栈进阶Next.js打造跨框架SaaS应用 #### 一、Next.js框架概述 Next.js是一个基于React的轻量级框架,主要用于构建高性能的应用程序。它在React的基础上添加了一系列强大的功能,如服务器端渲染(SSR)、...

    KVM实战:原理、进阶与性能调优.pdf

    云计算与虚拟化技术丛书《KVM实战:原理、进阶与性能调优》由任永杰、程舟编著,本系列书籍主要针对希望深入理解和应用KVM(Kernel-based Virtual Machine)技术...这对于云计算平台的构建者和维护者来说是至关重要的。

Global site tag (gtag.js) - Google Analytics