设计模式学习笔记(二十一:访问者模式)

1.1概述

    表示将一个作用于某对象结构中的各个元素的操作。它可以在不改变各个元素的类的前提下定义作用于这些元素的新操作。这就是访问者模式的定义。

    编写类的时候,可能在该类中编写了若干个实例方法,该类的对象通过调用这些实例方法操作其成员变量表明所产生的行为。在某些设计中,可能需要定义作用于类的成员变量的新操作,而且这个新操作不应当由该类中的某个实例方法来承担。

  例如,有一个电表(Ammeter)类,在电表类中,electricAmount成员变量的值表示用电量,showElectricAmount()方法返回electricAmount变量的值来收取电费。现在的问题是:希望根据用电量来计算电费,即根据electricAmount变量的值来收取电费。显然,不应该在Ammeter类中增加计算电费的方法(单表本身不能计算出电费)。在实际生活中,应当由物业部门的“计表员”观察电表的用电量,然后按着有关收费标准计算出电费。访问者模式建议让一个称作访问者的对象访问Ammeter对象(电表),以便定义作用于Ammeter对象上的操作。在访问者模式中,“计表员”是AmmeterVisitor类的实例,称作Ammeter类实例的访问者,AmmeterVisitor类中有一个计算电费的方法:

void visit(Ammeter ammeter);

  该方法的参数是Ammeter类的实例,因此只要将Ammeter类的实例传递给该方法的参数,AmmeterVisitor类的实例就可以计算出电费(PS:假设1度点0.88元):

double visit(Ammeter ammer){

  charge = ammeter.showElectricAmount()*0.88;

  return charge;

}

  按着访问者模式,应当在Ammeter类中增加一个接受访问者的方法。Ammeter类中接受访问者的方法可如下定义:

void accept(AmmeterVisitor v){

  v.visit(this);          //将自身传递给参数指定的访问者

}

  因此,一个Ammeter类的实例通过调用accept()方法,并向该方法传递一个访问者,即AmmeterVisitor的实例,然后Ammeter类的实例再将自身传递给访问者就可以知道自己需要交纳多少电费了。AmmeterAmmeterVisitor类的类图如下图一所示:

 

图一:有关缴纳电费关系类图(PS:类图画的有点不规范,仅作参考哦)

   Ammeter类有了接受访问者的accept(AmmeterVisitor visitor)方法后,可以不改变Ammeter类就能定义作用于Ammeter对象的成员变量上的新操作,比如,可以让accept(AmmeterVisitor visitor)的参数是AmmeterVisitor类的一个实例,该实例布局根据Ammeter对象的成员变量计算出正常的电费,而且还可以计算超电量应缴纳的额外电费。

 

1.2模式的结构

访问者模式包括以下五种角色:

(1)抽象元素(Element):一个抽象类,该类定义了接受访问者的accept操作。

(2)具体元素(Concrete Element):Element的子类。

(3)对象结构(Object Structure):一个集合,用于存放Element对象,提供遍历它自己的方法。

(4)抽象访问者(Visitor):一个接口,该接口定义操作对象(Concrete Element的实例)的方法。

(5)具体访问者(Concrete Visitor):实现Visitor接口的类。

访问者模式结构的类图如下图二所示:

 

图二:访问者模式的类图(PS:类图画的有点不规范,仅作参考哦)

 

 

1.3访问者模式的优点

1)可以在不改变一个集合中元素类的情况下,增加新的施加于该元素上的新操作。

2)可以将集合中各个元素的某些操作集中到访问者中,不仅便于集合的维护,也有利于集合中元素的复用。

 

 

1.4适合使用访问者模式的情景

(1)一个对象结构中,比如某个集合中,包含有很多对象,想对集合中对象增加新的操作。

(2)需要对集合中的对象进行很多不同并且不相关的操作,而又不想修改对象的类,就可以使用访问者模式。访问者模式可以在Visitor类中集中定义一些关于集合中对象的操作。

1.5访问者模式的使用

以下通过一个简单的问题来描述怎样使用访问者模式。这个简单的问题是:公司考核若干个大学生和研究生,以决定是否录用。在这个问题中,大学生和研究生都有自己的成绩,但是他们不能依据自己的成就制定录用标准,录用标准必须公司来确定。

 首先看一下本实例构建框架具体类和1.2模式的结构中类图的对应关系,如下图所示:

(1)抽象元素(Element

本问题中,抽象元素角色是Student类,代码如下:

package com.liuzhen.twenty_one_visitor;

public abstract class Sudent {
    public abstract void accept(Visitor v);
}

(2)具体元素(Concrete Element

本问题中,有两个具体元素,分别是UndergraduateGraduateStudent类,这两个类分别表示“大学生”和“研究生”,二者的成绩体系是不同的,代码如下:

 Undergraduate类代码:

package com.liuzhen.twenty_one_visitor;

public class Undergraduate extends Sudent {
    double math,english;
    String name;
    
    Undergraduate(String name,double math,double english){
        this.name = name;
        this.math = math;
        this.english = english;
    }
    
    public double getMath(){
        return math;
    }
    
    public double getEnglish(){
        return english;
    }
    
    public String getName(){
        return name;
    }
    
    public void accept(Visitor v){
        v.visit(this);
    }
}

GraduateStudent类代码:

package com.liuzhen.twenty_one_visitor;

public class GraduateStudent extends Sudent {
    double math,english,physics;
    String name;
    
    GraduateStudent(String name,double math,double english,double physics){
        this.name = name;
        this.math = math;
        this.english = english;
        this.physics = physics;
    }
    
    public double getMath(){
        return math;
    }
    
    public double getEnglish(){
        return english;
    }
    
    public String getName(){
        return name;
    }
    
    public double getPhysics(){
        return physics;
    }
    
    public void accept(Visitor v){
        v.visit(this);
    }
}

(3)对象结构(Object Structure

本问题中,该角色是java.util包中的ArrayList集合。

 

(4)抽象访问者(Visitor

本问题中,抽象访问者接口是Visitor,代码如下:

package com.liuzhen.twenty_one_visitor;

public interface Visitor {
    public void visit(Undergraduate stu);
    public void visit(GraduateStudent stu);
}

(5)具体访问者(Visitor

本问题中,具体访问者是公司Company,代码如下: 

package com.liuzhen.twenty_one_visitor;

public class Company implements Visitor {

    public void visit(Undergraduate stu) {
        double math = stu.getMath();
        double english = stu.getEnglish();
        if(math > 80 && english > 90)
            System.out.println(stu.getName()+"被录用");
    }

    public void visit(GraduateStudent stu) {
        double math = stu.getMath();
        double english = stu.getEnglish();
        double physics = stu.getPhysics();
        if(math > 80 && english > 90 && physics > 70)
            System.out.println(stu.getName()+"被录用");
    }

}

6)具体使用

  通过Twenty_oneApplication类来具体实现上述相关类和接口,来实现访问者模式的运用,其代码如下:

package com.liuzhen.twenty_one_visitor;

import java.util.ArrayList;
import java.util.Iterator;

public class Twenty_oneApplication {

    public static void main(String[] args) {
        Visitor visitor = new Company();
        ArrayList<Sudent> studentList = new ArrayList<Sudent>();
        Sudent student = null;
        studentList.add(student = new Undergraduate("张三",67,68));
        studentList.add(student = new Undergraduate("李四",80,88));
        studentList.add(student = new Undergraduate("王五",85,95));
        studentList.add(student = new GraduateStudent("小明",67,68,70));
        studentList.add(student = new GraduateStudent("小红",80,90,70));
        studentList.add(student = new GraduateStudent("小白",95,95,90));
        Iterator<Sudent> iter = studentList.iterator();
        while(iter.hasNext()){
            Sudent stu = iter.next();
            stu.accept(visitor);
        }
    }

}

运行结果:

王五被录用
小白被录用

参考资料:

      1.Java设计模式/耿祥义,张跃平著.——北京:清华大学出版社,2009.5

原文地址:https://www.cnblogs.com/liuzhen1995/p/6045015.html