访问者模式:在不改变一个已存在的类的层次结构的情况下,为这个层次结构中的某些类定义一个新的操作,这个新的操作作用于(操作)已存在的类对象,也即新的操作需要访问被作用的对象。
一句话: 为一个稳定的类结构增加操作. 也即把易变化的类的行为搬到访问者中.
特点:
1 访问者角色(Visitor)作用于具体的元素执行相关的操作;
2 元素角色(Element)定义了accept方法接受一个访问者,并通过调用Visitor的方法执行操作;
3 对象结构角色(Object structure)容许访问者访问每个元素。
个人认为访问者模式是最复杂最难理解的模式。
基本代码:
- //访问者接口
- package designpattern.visitor;
- public interface IVisitor {
- public void visitElement1(ConcreteElement1 e);
- public void visitElement2(ConcreteElement2 e);
- }
- //具体访问者1
- package designpattern.visitor;
- public class ConcreteVisitor1 implements IVisitor{
- public void visitElement1(ConcreteElement1 e) {
- System.out.println("ConcreteVisitor1 visit ConcreteElement1.");
- }
- public void visitElement2(ConcreteElement2 e) {
- System.out.println("ConcreteVisitor1 visit ConcreteElement2.");
- }
- }
- //具体访问者2
- package designpattern.visitor;
- public class ConcreteVisitor2 implements IVisitor{
- public void visitElement1(ConcreteElement1 e) {
- System.out.println("ConcreteVisitor2 visit ConcreteElement1.");
- }
- public void visitElement2(ConcreteElement2 e) {
- System.out.println("ConcreteVisitor2 visit ConcreteElement2.");
- }
- }
- //类结构借口
- package designpattern.visitor;
- public interface Element {
- public void accept(IVisitor v);
- }
- //实体类1
- package designpattern.visitor;
- public class ConcreteElement1 implements Element{
- public void accept(IVisitor v) {
- v.visitElement1(this);
- }
- }
- //实体类2
- package designpattern.visitor;
- public class ConcreteElement2 implements Element{
- public void accept(IVisitor v) {
- v.visitElement2(this);
- }
- }
- //对象结构
- package designpattern.visitor;
- import <a href="http://lib.csdn.net/base/17" class='replace_word' title="Java EE知识库" target='_blank' style='color:#df3434; font-weight:bold;'>Java</a>.util.*;
- public class ObjectsStructure {
- List<Element> list = new ArrayList();
- public ObjectsStructure(){
- list.add(new ConcreteElement1());
- list.add(new ConcreteElement2());
- }
- public void process(IVisitor v){
- for(Element e : list){
- e.accept(v);
- }
- }
- public static void main(String[] args){
- ObjectsStructure o = new ObjectsStructure();
- o.process(new ConcreteVisitor1());
- o.process(new ConcreteVisitor2());
- }
- }
再举一个例子,例子的来源于《大话设计模式》,作者试图从如下的几句话中抽象几个角色:
当男人成功时,背后多半有一个伟大的女人
当女人成功时,背后大多有一个不成功的男人
当男人失败时,闷头喝酒,谁也不用劝
当女人失败时,眼泪汪汪,谁劝也没用
当男人恋爱时,凡事不懂也装懂
当女人恋爱时,遇事懂也装不懂
。。。。
我们很容易写出这样的代码:
- public class Woman {
- public void getBehavior(String status){
- if("成功".equals(status)){
- System.out.println("背后大多有一个不成功的男人");
- }
- else if("失败".equals(status)){
- System.out.println("眼泪汪汪,谁劝也没用");
- }
- else if("恋爱".equals(status)){
- System.out.println("遇事懂也装不懂");
- }
- else if("结婚".equals(status)){
- //......
- }
- }
- }
- //
- public class Man {
- public void getBehavior(String status){
- if("成功".equals(status)){
- System.out.println("背后多半有一个伟大的女人");
- }
- else if("失败".equals(status)){
- System.out.println("闷头喝酒,谁也不用劝");
- }
- else if("恋爱".equals(status)){
- System.out.println("凡事不懂也装懂");
- }
- else if("结婚".equals(status)){
- //......
- }
- }
- }
但是这样写出现了两个问题:
1 代码中有坏味道, if else充斥着整个方法体;
2 外部状态(成功,失败,恋爱,结婚....)是非稳定的因素, 他们可能随时会改变, 可能随时会增加, 如果增加了新的状态, 比如生病,升迁,当官等, 我们就得修改代码, 这就违背了开放封闭原则(关于开放封闭原则, 将在后面写blog解释).
因为人分两种, 男人, 女人, 这种类结构几乎不会发生变化, 而外在的状态(成功,失败,恋爱,结婚....)却可以随时改变, 所以我们用访问者模式来改进上面的代码:
- //
- package designpattern.visitor;
- public interface Person {
- public void accept(Visitor v);
- }
- //
- package designpattern.visitor;
- public class Man implements Person{
- public void accept(Visitor v) {
- v.visit(this);
- }
- }
- //
- package designpattern.visitor;
- public class Woman implements Person{
- public void accept(Visitor v) {
- v.visit(this);
- }
- }
- //
- package designpattern.visitor;
- public interface Visitor {
- public void visit(Man m);
- public void visit (Woman w);
- }
- //
- package designpattern.visitor;
- public class Success implements Visitor {
- public void visit(Man m) {
- System.out.println("当男人成功时,背后多半有一个伟大的女人");
- }
- public void visit(Woman w) {
- System.out.println("当女人成功时,背后大多有一个不成功的男人");
- }
- }
- //
- package designpattern.visitor;
- public class Love implements Visitor{
- public void visit(Man m) {
- System.out.println("当男人恋爱时,凡事不懂也装懂");
- }
- public void visit(Woman w) {
- System.out.println("当女人恋爱时,遇事懂也装不懂");
- }
- }
- //
- package designpattern.visitor;
- public class Failure implements Visitor{
- public void visit(Man m) {
- System.out.println("当男人失败时,闷头喝酒,谁也不用劝");
- }
- public void visit(Woman w) {
- System.out.println("当女人失败时,眼泪汪汪,谁劝也没用");
- }
- }
再举一例,我们假设银行只有两种经典业务:存款,取款,这两种业务种类自从有银行开始都没有变过, 所以他们是稳定因素,然而随着时代的变迁,银行的客户却在逐渐细分,增加。过去有普通存款,取款类型, 公司存款取款类型,现在有了一些新的客户群,比如股票第三方存取款客户,代理基金业务客户等。而且银行在不断的开辟新的客户。 可以认为,银行的服务结构没有变,变的是外在的客户,可以用访问者模式。
- //服务接口
- package designpattern.visitor.bank;
- public abstract class Service {
- public abstract void accept(Visitor v);
- public int getCounterNum(){
- //得到可用的服务柜台,现实中的算法可以是通过看哪个柜台空闲就到哪个柜台,或者是按照一定的规则排序。
- //这里为了简化,就用随机数。
- return (int) Math.floor((Math.random()*4));
- }
- }
- //访问者接口
- package designpattern.visitor.bank;
- public interface Visitor {
- public void visit(DrawService s);
- public void visit(SavingService s);
- }
- //
- package designpattern.visitor.bank;
- public class DrawService extends Service {
- public void accept(Visitor v) {
- v.visit(this);
- }
- }
- //
- package designpattern.visitor.bank;
- public class SavingService extends Service {
- public void accept(Visitor v) {
- v.visit(this);
- }
- }
- //
- //普通存款取款访问者
- package designpattern.visitor.bank;
- public class CommonVisitor implements Visitor {
- public void visit(DrawService s) {
- System.out.println("您办理的是普通取款业务。" + "请到 " + s.getCounterNum() + " 窗口。");
- }
- public void visit(SavingService s) {
- System.out.println("您办理的是普通存款业务。" + "请到 " + s.getCounterNum() + " 窗口。");
- }
- }
- //
- //公司业务办理者
- package designpattern.visitor.bank;
- public class CompanyVisitor implements Visitor{
- public void visit(DrawService s) {
- System.out.println("您办理的是公司取款业务。" + "请到 " + s.getCounterNum() + " 窗口。");
- }
- public void visit(SavingService s) {
- System.out.println("您办理的是公司存款业务。" + "请到 " + s.getCounterNum() + " 窗口。");
- }
- }
- //
- //股票第三方存款业务办理者
- package designpattern.visitor.bank;
- public class StockVisitor implements Visitor {
- public void visit(DrawService s) {
- System.out.println("您办理的是股票交易第三方取款业务。" + "请到 " + s.getCounterNum() + " 窗口。");
- }
- public void visit(SavingService s) {
- System.out.println("您办理的是股票交易第三方存款业务。" + "请到 " + s.getCounterNum() + " 窗口。");
- }
- }
- //
- //基金买卖委托业务办理者
- package designpattern.visitor.bank;
- public class FundVisitor implements Visitor {
- public void visit(DrawService s) {
- System.out.println("您办理的是基金委托买入业务。" + "请到 " + s.getCounterNum() + " 窗口。");
- }
- public void visit(SavingService s) {
- System.out.println("您办理的是基金委托卖出业务。" + "请到 " + s.getCounterNum() + " 窗口。");
- }
- }
优点:
对类结构增加新的操作很容易,只需要增加一个新的访问者。
缺点:
1 复杂
2 当数据结构或类发生变化的时候改动太大。
经典案例:
找ing。。。