`

23种设计模式(9):访问者模式

 
阅读更多

定义:封装某些作用于某种数据结构中各元素的操作,它可以在不改变数据结构的前提下定义作用于这些元素的新的操作。

类型:行为类模式

类图:

访问者模式可能是行为类模式中最复杂的一种模式了,但是这不能成为我们不去掌握它的理由。我们首先来看一个简单的例子,代码如下:

  1. classA{
  2. publicvoidmethod1(){
  3. System.out.println("我是A");
  4. }
  5. publicvoidmethod2(Bb){
  6. b.showA(this);
  7. }
  8. }
  9. classB{
  10. publicvoidshowA(Aa){
  11. a.method1();
  12. }
  13. }

我们主要来看一下在类A中,方法method1和方法method2的区别在哪里,方法method1很简单,就是打印出一句“我是A”;方法method2稍微复杂一点,使用类B作为参数,并调用类B的showA方法。再来看一下类B的showA方法,showA方法使用类A作为参数,然后调用类A的method1方法,可以看到,method2方法绕来绕去,无非就是调用了一下自己的method1方法而已,它的运行结果应该也是“我是A”,分析完之后,我们来运行一下这两个方法,并看一下运行结果:

  1. publicclassTest{
  2. publicstaticvoidmain(String[]args){
  3. Aa=newA();
  4. a.method1();
  5. a.method2(newB());
  6. }
  7. }

运行结果为:

我是A
我是A

看懂了这个例子,就理解了访问者模式的90%,在例子中,对于类A来说,类B就是一个访问者。但是这个例子并不是访问者模式的全部,虽然直观,但是它的可扩展性比较差,下面我们就来说一下访问者模式的通用实现,通过类图可以看到,在访问者模式中,主要包括下面几个角色:

  • 抽象访问者:抽象类或者接口,声明访问者可以访问哪些元素,具体到程序中就是visit方法中的参数定义哪些对象是可以被访问的。
  • 访问者:实现抽象访问者所声明的方法,它影响到访问者访问到一个类后该干什么,要做什么事情。
  • 抽象元素类:接口或者抽象类,声明接受哪一类访问者访问,程序上是通过accept方法中的参数来定义的。抽象元素一般有两类方法,一部分是本身的业务逻辑,另外就是允许接收哪类访问者来访问。
  • 元素类:实现抽象元素类所声明的accept方法,通常都是visitor.visit(this),基本上已经形成一种定式了。
  • 结构对象:一个元素的容器,一般包含一个容纳多个不同类、不同接口的容器,如List、Set、Map等,在项目中一般很少抽象出这个角色。

访问者模式的通用代码实现

  1. abstractclassElement{
  2. publicabstractvoidaccept(IVisitorvisitor);
  3. publicabstractvoiddoSomething();
  4. }
  5. interfaceIVisitor{
  6. publicvoidvisit(ConcreteElement1el1);
  7. publicvoidvisit(ConcreteElement2el2);
  8. }
  9. classConcreteElement1extendsElement{
  10. publicvoiddoSomething(){
  11. System.out.println("这是元素1");
  12. }
  13. publicvoidaccept(IVisitorvisitor){
  14. visitor.visit(this);
  15. }
  16. }
  17. classConcreteElement2extendsElement{
  18. publicvoiddoSomething(){
  19. System.out.println("这是元素2");
  20. }
  21. publicvoidaccept(IVisitorvisitor){
  22. visitor.visit(this);
  23. }
  24. }
  25. classVisitorimplementsIVisitor{
  26. publicvoidvisit(ConcreteElement1el1){
  27. el1.doSomething();
  28. }
  29. publicvoidvisit(ConcreteElement2el2){
  30. el2.doSomething();
  31. }
  32. }
  33. classObjectStruture{
  34. publicstaticList<Element>getList(){
  35. List<Element>list=newArrayList<Element>();
  36. Randomran=newRandom();
  37. for(inti=0;i<10;i++){
  38. inta=ran.nextInt(100);
  39. if(a>50){
  40. list.add(newConcreteElement1());
  41. }else{
  42. list.add(newConcreteElement2());
  43. }
  44. }
  45. returnlist;
  46. }
  47. }
  48. publicclassClient{
  49. publicstaticvoidmain(String[]args){
  50. List<Element>list=ObjectStruture.getList();
  51. for(Elemente:list){
  52. e.accept(newVisitor());
  53. }
  54. }
  55. }


访问者模式的优点

  • 符合单一职责原则:凡是适用访问者模式的场景中,元素类中需要封装在访问者中的操作必定是与元素类本身关系不大且是易变的操作,使用访问者模式一方面符合单一职责原则,另一方面,因为被封装的操作通常来说都是易变的,所以当发生变化时,就可以在不改变元素类本身的前提下,实现对变化部分的扩展。
  • 扩展性良好:元素类可以通过接受不同的访问者来实现对不同操作的扩展。

访问者模式的适用场景

假如一个对象中存在着一些与本对象不相干(或者关系较弱)的操作,为了避免这些操作污染这个对象,则可以使用访问者模式来把这些操作封装到访问者中去。

假如一组对象中,存在着相似的操作,为了避免出现大量重复的代码,也可以将这些重复的操作封装到访问者中去。

但是,访问者模式并不是那么完美,它也有着致命的缺陷:增加新的元素类比较困难。通过访问者模式的代码可以看到,在访问者类中,每一个元素类都有它对应的处理方法,也就是说,每增加一个元素类都需要修改访问者类(也包括访问者类的子类或者实现类),修改起来相当麻烦。也就是说,在元素类数目不确定的情况下,应该慎用访问者模式。所以,访问者模式比较适用于对已有功能的重构,比如说,一个项目的基本功能已经确定下来,元素类的数据已经基本确定下来不会变了,会变的只是这些元素内的相关操作,这时候,我们可以使用访问者模式对原有的代码进行重构一遍,这样一来,就可以在不修改各个元素类的情况下,对原有功能进行修改。

总结

正如《设计模式》的作者GoF对访问者模式的描述:大多数情况下,你并需要使用访问者模式,但是当你一旦需要使用它时,那你就是真的需要它了。当然这只是针对真正的大牛而言。在现实情况下(至少是我所处的环境当中),很多人往往沉迷于设计模式,他们使用一种设计模式时,从来不去认真考虑所使用的模式是否适合这种场景,而往往只是想展示一下自己对面向对象设计的驾驭能力。编程时有这种心理,往往会发生滥用设计模式的情况。所以,在学习设计模式时,一定要理解模式的适用性。必须做到使用一种模式是因为了解它的优点,不使用一种模式是因为了解它的弊端;而不是使用一种模式是因为不了解它的弊端,不使用一种模式是因为不了解它的优点。

定义:封装某些作用于某种数据结构中各元素的操作,它可以在不改变数据结构的前提下定义作用于这些元素的新的操作。

类型:行为类模式

类图:

访问者模式可能是行为类模式中最复杂的一种模式了,但是这不能成为我们不去掌握它的理由。我们首先来看一个简单的例子,代码如下:

  1. classA{
  2. publicvoidmethod1(){
  3. System.out.println("我是A");
  4. }
  5. publicvoidmethod2(Bb){
  6. b.showA(this);
  7. }
  8. }
  9. classB{
  10. publicvoidshowA(Aa){
  11. a.method1();
  12. }
  13. }

我们主要来看一下在类A中,方法method1和方法method2的区别在哪里,方法method1很简单,就是打印出一句“我是A”;方法method2稍微复杂一点,使用类B作为参数,并调用类B的showA方法。再来看一下类B的showA方法,showA方法使用类A作为参数,然后调用类A的method1方法,可以看到,method2方法绕来绕去,无非就是调用了一下自己的method1方法而已,它的运行结果应该也是“我是A”,分析完之后,我们来运行一下这两个方法,并看一下运行结果:

  1. publicclassTest{
  2. publicstaticvoidmain(String[]args){
  3. Aa=newA();
  4. a.method1();
  5. a.method2(newB());
  6. }
  7. }

运行结果为:

我是A
我是A

看懂了这个例子,就理解了访问者模式的90%,在例子中,对于类A来说,类B就是一个访问者。但是这个例子并不是访问者模式的全部,虽然直观,但是它的可扩展性比较差,下面我们就来说一下访问者模式的通用实现,通过类图可以看到,在访问者模式中,主要包括下面几个角色:

  • 抽象访问者:抽象类或者接口,声明访问者可以访问哪些元素,具体到程序中就是visit方法中的参数定义哪些对象是可以被访问的。
  • 访问者:实现抽象访问者所声明的方法,它影响到访问者访问到一个类后该干什么,要做什么事情。
  • 抽象元素类:接口或者抽象类,声明接受哪一类访问者访问,程序上是通过accept方法中的参数来定义的。抽象元素一般有两类方法,一部分是本身的业务逻辑,另外就是允许接收哪类访问者来访问。
  • 元素类:实现抽象元素类所声明的accept方法,通常都是visitor.visit(this),基本上已经形成一种定式了。
  • 结构对象:一个元素的容器,一般包含一个容纳多个不同类、不同接口的容器,如List、Set、Map等,在项目中一般很少抽象出这个角色。

访问者模式的通用代码实现

  1. abstractclassElement{
  2. publicabstractvoidaccept(IVisitorvisitor);
  3. publicabstractvoiddoSomething();
  4. }
  5. interfaceIVisitor{
  6. publicvoidvisit(ConcreteElement1el1);
  7. publicvoidvisit(ConcreteElement2el2);
  8. }
  9. classConcreteElement1extendsElement{
  10. publicvoiddoSomething(){
  11. System.out.println("这是元素1");
  12. }
  13. publicvoidaccept(IVisitorvisitor){
  14. visitor.visit(this);
  15. }
  16. }
  17. classConcreteElement2extendsElement{
  18. publicvoiddoSomething(){
  19. System.out.println("这是元素2");
  20. }
  21. publicvoidaccept(IVisitorvisitor){
  22. visitor.visit(this);
  23. }
  24. }
  25. classVisitorimplementsIVisitor{
  26. publicvoidvisit(ConcreteElement1el1){
  27. el1.doSomething();
  28. }
  29. publicvoidvisit(ConcreteElement2el2){
  30. el2.doSomething();
  31. }
  32. }
  33. classObjectStruture{
  34. publicstaticList<Element>getList(){
  35. List<Element>list=newArrayList<Element>();
  36. Randomran=newRandom();
  37. for(inti=0;i<10;i++){
  38. inta=ran.nextInt(100);
  39. if(a>50){
  40. list.add(newConcreteElement1());
  41. }else{
  42. list.add(newConcreteElement2());
  43. }
  44. }
  45. returnlist;
  46. }
  47. }
  48. publicclassClient{
  49. publicstaticvoidmain(String[]args){
  50. List<Element>list=ObjectStruture.getList();
  51. for(Elemente:list){
  52. e.accept(newVisitor());
  53. }
  54. }
  55. }


访问者模式的优点

  • 符合单一职责原则:凡是适用访问者模式的场景中,元素类中需要封装在访问者中的操作必定是与元素类本身关系不大且是易变的操作,使用访问者模式一方面符合单一职责原则,另一方面,因为被封装的操作通常来说都是易变的,所以当发生变化时,就可以在不改变元素类本身的前提下,实现对变化部分的扩展。
  • 扩展性良好:元素类可以通过接受不同的访问者来实现对不同操作的扩展。

访问者模式的适用场景

假如一个对象中存在着一些与本对象不相干(或者关系较弱)的操作,为了避免这些操作污染这个对象,则可以使用访问者模式来把这些操作封装到访问者中去。

假如一组对象中,存在着相似的操作,为了避免出现大量重复的代码,也可以将这些重复的操作封装到访问者中去。

但是,访问者模式并不是那么完美,它也有着致命的缺陷:增加新的元素类比较困难。通过访问者模式的代码可以看到,在访问者类中,每一个元素类都有它对应的处理方法,也就是说,每增加一个元素类都需要修改访问者类(也包括访问者类的子类或者实现类),修改起来相当麻烦。也就是说,在元素类数目不确定的情况下,应该慎用访问者模式。所以,访问者模式比较适用于对已有功能的重构,比如说,一个项目的基本功能已经确定下来,元素类的数据已经基本确定下来不会变了,会变的只是这些元素内的相关操作,这时候,我们可以使用访问者模式对原有的代码进行重构一遍,这样一来,就可以在不修改各个元素类的情况下,对原有功能进行修改。

总结

正如《设计模式》的作者GoF对访问者模式的描述:大多数情况下,你并需要使用访问者模式,但是当你一旦需要使用它时,那你就是真的需要它了。当然这只是针对真正的大牛而言。在现实情况下(至少是我所处的环境当中),很多人往往沉迷于设计模式,他们使用一种设计模式时,从来不去认真考虑所使用的模式是否适合这种场景,而往往只是想展示一下自己对面向对象设计的驾驭能力。编程时有这种心理,往往会发生滥用设计模式的情况。所以,在学习设计模式时,一定要理解模式的适用性。必须做到使用一种模式是因为了解它的优点,不使用一种模式是因为了解它的弊端;而不是使用一种模式是因为不了解它的弊端,不使用一种模式是因为不了解它的优点。

分享到:
评论

相关推荐

    《设计模式:可复用面向对象软件的基础》学习并理解 23 种设计模式

    - **访问者模式**(Visitor):定义一个作用于某对象结构中的各元素的操作。 ### 使用设计模式的实际例子 以**组合模式**为例,假设我们需要设计一个系统来管理各种类型的电脑产品。我们可以定义一个抽象的`...

    23种设计模式之:建造者,代理,观察,策略,状态模式

    本文将重点介绍标题中提到的五种设计模式:建造者模式、代理模式、观察者模式、策略模式以及状态模式。 **1. 建造者模式(Builder)** 建造者模式是一种创建型设计模式,它允许我们分步骤构建复杂对象,而无需暴露...

    GOF23种设计模式

    GOF(GoF)23种设计模式,是由Erich Gamma、Richard Helm、Ralph Johnson和John Vlissides四位大神在他们的著作《设计模式:可复用面向对象软件的基础》中提出的,这些模式分为创建型、结构型和行为型三大类。...

    java 23种设计模式.zip

    设计模式主要分为三大类: 1.创建型模式:工厂模式、抽象...4.行为型模式:模板方法模式、命令模式、迭代器模式、观察者模式、中介者模式、备忘录模式、解释器模式、状态模式、策略模式、职责链模式、访问者模式。

    23种设计模式(C++).pdf

    设计模式分为创建型、结构型和行为型三类,总共包含23种模式。在C++中实现这些模式,不仅可以提升代码的复用性,还能提高软件的质量和可维护性。 创建型模式包括: - Factory模式:定义一个用于创建对象的接口,让...

    C++ 23种设计模式1

    《C++ 23种设计模式1》是关于软件工程中设计模式的深入解析,主要聚焦于C++语言的实现。设计模式是经过时间和实践验证的解决方案,它们针对常见的编程问题提供了一套标准的模板,使得开发者能够更高效地编写可复用、...

    23种 设计模式

    - 访问者模式:定义一个访问者接口,用来访问聚合对象的元素,同时给这些元素增加新的操作而不影响其结构。 这23种设计模式是软件开发中的宝贵财富,学习和理解它们能帮助开发者写出更高效、易于维护的代码。在...

    23种java设计模式.pdf

    JAVA 设计模式可以分为三种:创建模式、结构模式和行为模式。 1. 创建模式 创建模式是指在创建对象时使用的模式,包括 Factory(工厂模式)、Singleton(单例模式)、Builder(建造者模式)、Prototype(原型模式...

    java23种设计模式详解+源码(绝对详解)

    在Java编程中,有23种经典的GoF(Gang of Four)设计模式,它们被分为三大类:创建型、结构型和行为型。本资源集合了这些模式的详细解释与源码分析,旨在帮助开发者深入理解和应用设计模式。 1. 创建型模式...

    Java之23种设计模式解析

    包括责任链模式、命令模式、解释器模式、迭代器模式、备忘录模式、观察者模式、状态模式、策略模式、模板方法模式和访问者模式。它们可以帮助我们更好地管理复杂系统的动态行为。 每一种设计模式都有其特定的适用...

    23种设计模式文档

    17. **访问者模式**:在不改变集合元素的前提下,为一个集合中的每个元素增加新的操作。 18. **模态模式**:定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都会得到通知并被...

    Java 23种设计模式27访问者模式.pdf

    访问者模式(Visitor Pattern)是一种行为型设计模式,它允许我们向现有的类层次结构中添加新的功能,而无需修改这些类。该模式通过将这些功能封装成独立的类(即访问者),然后让这些访问者访问并操作这些现有类的...

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

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

    C# 23种设计模式(中文高清带书签)

    《C# 23种设计模式》是一本深入解析C#编程中常用设计模式的权威指南,对于想要提升代码质量、提高软件可维护性的开发者来说,是不可或缺的参考资料。书中详细介绍了23种经典的设计模式,这些模式是软件工程实践中...

    23种面向对象设计模式

    3. **行为型模式**(Behavioral Patterns):关注对象之间的责任分配和通信,包括策略模式(Strategy)、模板方法模式(Template Method)、迭代器模式(Iterator)、观察者模式(Observer)、访问者模式(Visitor)...

    23种设计模式demo

    java的设计模式大体上分为三大类: ...行为型模式(11种):策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。

    java之23种设计模式完整代码

    - **访问者模式**:在不改变集合元素的前提下,为集合中的元素增加新的操作。 - **备忘录模式**:在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,以便以后恢复对象的先前状态。 -...

    java23种设计模式

    java23种设计模式,每一种模式都有详细的讲解,很全面,如果你想深入了解一下java23种设计模式,这会非常适合你的哦!上传乃为百度云连接,失效请留言。 内容: 001策略模式! N. B8 ~' D! f9 j+ g0 I 002观察者模式 ...

    23种设计模式详解PDF

    设计模式 的分类 总体来说设计模式分为三大类: 创建型模式(5): ...策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。

Global site tag (gtag.js) - Google Analytics