访问者模式(Visitor Patten)

参考文章:

http://www.importnew.com/15561.html

 

定义:

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

 

uml类图:

模式组成:

  • Vsitor :访问者的抽象角色抽象类或者接口,声明访问者可以访问哪些元素,具体到程序中就是visit方法中的参数定义哪些对象是可以被访问的。
  • ConcreteVsitor:访问者的具体实现实现抽象访问者所声明的方法,它影响到访问者访问到一个类后该干什么,要做什么事情。
  • Element:元素的抽象角色接口或者抽象类,声明接受哪一类访问者访问,程序上是通过accept方法中的参数来定义的。抽象元素一般有两类方法,一部分是本身的业务逻辑,另外就是允许接收哪类访问者来访问。
  • ConcreteElement:元素的具体实现实现抽象元素类所声明的accept方法,通常都是visitor.visit(this),基本上已经形成一种定式了。

 

优点:

  • 符合单一职责原则凡是适用访问者模式的场景中,元素类中需要封装在访问者中的操作必定是与元素类本身关系不大且是易变的操作,使用访问者模式符合单一职责原则
  • 扩展性良好元素类可以通过接受不同的访问者来实现对不同操作的扩展。

缺点:

  • 增加新的元素类比较困难。

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

 

应用场景:

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

 

扩展:

 

java的动态绑定与双分派

http://blog.csdn.net/zhengzhb/article/details/7496949

 

举个栗子:

(对账单定义不同的访问者。访问者老板关注的点是盈利总额。访问者会计关注的点是账单明细与工资明细是否匹配)

 

定义访问者的抽象角色

interface Visitor {
    public void visit(Element element);
}

定义元素的抽象角色

interface Element {
    public void accept(Visitor visitor);
}

定义元素的具体实现

class Bill implements Element {
    public int totalGain = 500;
    public boolean isMatchSalaryDetail = false;

    public int getTotalGain() {
        return totalGain;
    }

    public boolean isMatchSalaryDetail() {
        return isMatchSalaryDetail;
    }

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

定义一个访问者

class Boss implements Visitor {
    @Override
    public void visit(Element element) {
        Bill bill = (Bill) element;
        if (bill.getTotalGain() < 3000) {
            dosomething();
        }
    }

    public void dosomething() {
        System.out.println("老板 发现盈利很低,很生气,准备全体降薪!");
    }
}

定义一个访问者

class Accounting implements Visitor {
    @Override
    public void visit(Element element) {
        Bill bill = (Bill) element;
        if (!bill.isMatchSalaryDetail) {
            dosomething();
        }
    }
    public void dosomething() {
        System.out.println("会计 发现账单明细与工资明细不匹配!立刻查账!");
    }
}

客户端调用

public static void main(String[] args) {
        Visitor boss = new Boss();
        Visitor accounting = new Accounting();
        Element bill=new Bill();
        bill.accept(boss);
        bill.accept(accounting);
    }

输出

posted @ 2017-12-01 16:46  情歌z  阅读(176)  评论(0编辑  收藏  举报