设计模式之访问者模式(Visitor)

访问者模式:在不改变一个已存在的类的层次结构的情况下,为这个层次结构中的某些类定义一个新的操作,这个新的操作作用于(操作)已存在的类对象,也即新的操作需要访问被作用的对象。

一句话: 为一个稳定的类结构增加操作. 也即把易变化的类的行为搬到访问者中.

特点:

1 访问者角色(Visitor)作用于具体的元素执行相关的操作;

2 元素角色(Element)定义了accept方法接受一个访问者,并通过调用Visitor的方法执行操作;

3 对象结构角色(Object structure)容许访问者访问每个元素。

个人认为访问者模式是最复杂最难理解的模式。

基本代码:

[java]  view plain  copy
  1. //访问者接口  
  2. package designpattern.visitor;  
  3.   
  4. public interface IVisitor {  
  5.     public void visitElement1(ConcreteElement1 e);  
  6.     public void visitElement2(ConcreteElement2 e);  
  7. }  
  8. //具体访问者1  
  9. package designpattern.visitor;  
  10.   
  11. public class ConcreteVisitor1 implements IVisitor{  
  12.     public void visitElement1(ConcreteElement1 e) {  
  13.         System.out.println("ConcreteVisitor1 visit ConcreteElement1.");  
  14.     }  
  15.   
  16.     public void visitElement2(ConcreteElement2 e) {  
  17.         System.out.println("ConcreteVisitor1 visit ConcreteElement2.");  
  18.     }  
  19. }  
  20. //具体访问者2  
  21. package designpattern.visitor;  
  22.   
  23. public class ConcreteVisitor2 implements IVisitor{  
  24.     public void visitElement1(ConcreteElement1 e) {  
  25.         System.out.println("ConcreteVisitor2 visit ConcreteElement1.");  
  26.     }  
  27.   
  28.     public void visitElement2(ConcreteElement2 e) {  
  29.         System.out.println("ConcreteVisitor2 visit ConcreteElement2.");  
  30.     }  
  31. }  
  32. //类结构借口   
  33. package designpattern.visitor;  
  34.   
  35. public interface Element {  
  36.     public void accept(IVisitor v);  
  37. }  
  38. //实体类1  
  39. package designpattern.visitor;  
  40.   
  41. public class ConcreteElement1 implements Element{  
  42.     public void accept(IVisitor v) {  
  43.         v.visitElement1(this);  
  44.     }  
  45. }  
  46. //实体类2  
  47. package designpattern.visitor;  
  48.   
  49. public class ConcreteElement2 implements Element{  
  50.     public void accept(IVisitor v) {  
  51.         v.visitElement2(this);  
  52.     }  
  53. }  
  54.   
  55. //对象结构  
  56. package designpattern.visitor;  
  57. 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.*;  
  58.   
  59. public class ObjectsStructure {  
  60.     List<Element> list = new ArrayList();  
  61.       
  62.     public ObjectsStructure(){  
  63.         list.add(new ConcreteElement1());  
  64.         list.add(new ConcreteElement2());  
  65.     }  
  66.     public void process(IVisitor v){  
  67.         for(Element e : list){  
  68.             e.accept(v);  
  69.         }  
  70.     }  
  71.     public static void main(String[] args){  
  72.         ObjectsStructure o = new ObjectsStructure();  
  73.         o.process(new ConcreteVisitor1());  
  74.         o.process(new ConcreteVisitor2());  
  75.     }  
  76. }  

再举一个例子,例子的来源于《大话设计模式》,作者试图从如下的几句话中抽象几个角色:

当男人成功时,背后多半有一个伟大的女人

当女人成功时,背后大多有一个不成功的男人

当男人失败时,闷头喝酒,谁也不用劝

当女人失败时,眼泪汪汪,谁劝也没用

当男人恋爱时,凡事不懂也装懂

当女人恋爱时,遇事懂也装不懂

。。。。

我们很容易写出这样的代码:

[java]  view plain  copy
  1. public class Woman {  
  2.     public void getBehavior(String status){  
  3.         if("成功".equals(status)){  
  4.             System.out.println("背后大多有一个不成功的男人");  
  5.         }  
  6.         else if("失败".equals(status)){  
  7.             System.out.println("眼泪汪汪,谁劝也没用");  
  8.         }  
  9.         else if("恋爱".equals(status)){  
  10.             System.out.println("遇事懂也装不懂");  
  11.         }  
  12.         else if("结婚".equals(status)){  
  13.             //......  
  14.         }  
  15.     }  
  16. }  
  17. //  
  18. public class Man {  
  19.     public void getBehavior(String status){  
  20.         if("成功".equals(status)){  
  21.             System.out.println("背后多半有一个伟大的女人");  
  22.         }  
  23.         else if("失败".equals(status)){  
  24.             System.out.println("闷头喝酒,谁也不用劝");  
  25.         }  
  26.         else if("恋爱".equals(status)){  
  27.             System.out.println("凡事不懂也装懂");  
  28.         }  
  29.         else if("结婚".equals(status)){  
  30.             //......  
  31.         }  
  32.     }  
  33. }  

但是这样写出现了两个问题:

1 代码中有坏味道, if else充斥着整个方法体;

2 外部状态(成功,失败,恋爱,结婚....)是非稳定的因素, 他们可能随时会改变, 可能随时会增加, 如果增加了新的状态, 比如生病,升迁,当官等, 我们就得修改代码, 这就违背了开放封闭原则(关于开放封闭原则, 将在后面写blog解释).

 

因为人分两种, 男人, 女人, 这种类结构几乎不会发生变化, 而外在的状态(成功,失败,恋爱,结婚....)却可以随时改变, 所以我们用访问者模式来改进上面的代码:

[java]  view plain  copy
  1. //  
  2. package designpattern.visitor;  
  3.   
  4. public interface Person {  
  5.     public void accept(Visitor v);  
  6. }  
  7. //  
  8. package designpattern.visitor;  
  9.   
  10. public class Man implements Person{  
  11.     public void accept(Visitor v) {  
  12.         v.visit(this);  
  13.     }  
  14. }  
  15. //  
  16. package designpattern.visitor;  
  17.   
  18. public class Woman implements Person{  
  19.     public void accept(Visitor v) {  
  20.         v.visit(this);  
  21.     }  
  22. }  
  23. //  
  24. package designpattern.visitor;  
  25.   
  26. public interface Visitor {  
  27.     public void visit(Man m);  
  28.     public void visit (Woman w);  
  29. }  
  30. //  
  31. package designpattern.visitor;  
  32.   
  33. public class Success implements Visitor {  
  34.     public void visit(Man m) {  
  35.         System.out.println("当男人成功时,背后多半有一个伟大的女人");  
  36.     }  
  37.     public void visit(Woman w) {  
  38.         System.out.println("当女人成功时,背后大多有一个不成功的男人");  
  39.     }  
  40.   
  41. }  
  42. //  
  43. package designpattern.visitor;  
  44.   
  45. public class Love implements Visitor{  
  46.     public void visit(Man m) {  
  47.         System.out.println("当男人恋爱时,凡事不懂也装懂");  
  48.     }  
  49.   
  50.     public void visit(Woman w) {  
  51.         System.out.println("当女人恋爱时,遇事懂也装不懂");  
  52.     }  
  53.   
  54. }  
  55. //  
  56. package designpattern.visitor;  
  57.   
  58. public class Failure implements Visitor{  
  59.     public void visit(Man m) {  
  60.         System.out.println("当男人失败时,闷头喝酒,谁也不用劝");  
  61.     }  
  62.   
  63.     public void visit(Woman w) {  
  64.         System.out.println("当女人失败时,眼泪汪汪,谁劝也没用");  
  65.     }  
  66. }  

再举一例,我们假设银行只有两种经典业务:存款,取款,这两种业务种类自从有银行开始都没有变过, 所以他们是稳定因素,然而随着时代的变迁,银行的客户却在逐渐细分,增加。过去有普通存款,取款类型, 公司存款取款类型,现在有了一些新的客户群,比如股票第三方存取款客户,代理基金业务客户等。而且银行在不断的开辟新的客户。 可以认为,银行的服务结构没有变,变的是外在的客户,可以用访问者模式。

[java]  view plain  copy
  1. //服务接口  
  2. package designpattern.visitor.bank;  
  3. public abstract class Service {  
  4.     public abstract void accept(Visitor v);  
  5.     public int getCounterNum(){  
  6.         //得到可用的服务柜台,现实中的算法可以是通过看哪个柜台空闲就到哪个柜台,或者是按照一定的规则排序。  
  7.         //这里为了简化,就用随机数。  
  8.         return (int) Math.floor((Math.random()*4));  
  9.     }  
  10. }  
  11. //访问者接口  
  12. package designpattern.visitor.bank;  
  13. public interface Visitor {  
  14.     public void visit(DrawService s);  
  15.     public void visit(SavingService s);  
  16. }  
  17. //  
  18. package designpattern.visitor.bank;  
  19. public class DrawService extends Service {  
  20.     public void accept(Visitor v) {  
  21.         v.visit(this);  
  22.     }  
  23. }  
  24. //  
  25. package designpattern.visitor.bank;  
  26. public class SavingService extends Service {  
  27.     public void accept(Visitor v) {  
  28.         v.visit(this);  
  29.     }  
  30. }  
  31. //  
  32. //普通存款取款访问者  
  33. package designpattern.visitor.bank;  
  34. public class CommonVisitor implements Visitor {  
  35.     public void visit(DrawService s) {  
  36.         System.out.println("您办理的是普通取款业务。" + "请到 " + s.getCounterNum() + " 窗口。");  
  37.     }  
  38.   
  39.     public void visit(SavingService s) {  
  40.         System.out.println("您办理的是普通存款业务。"  + "请到 " + s.getCounterNum() + " 窗口。");  
  41.     }  
  42.   
  43. }  
  44. //  
  45. //公司业务办理者  
  46. package designpattern.visitor.bank;  
  47. public class CompanyVisitor implements Visitor{  
  48.     public void visit(DrawService s) {  
  49.         System.out.println("您办理的是公司取款业务。" + "请到 " + s.getCounterNum() + " 窗口。");  
  50.     }  
  51.   
  52.     public void visit(SavingService s) {  
  53.         System.out.println("您办理的是公司存款业务。" + "请到 " + s.getCounterNum() + " 窗口。");  
  54.     }  
  55.   
  56. }  
  57. //  
  58. //股票第三方存款业务办理者  
  59. package designpattern.visitor.bank;  
  60. public class StockVisitor implements Visitor {  
  61.     public void visit(DrawService s) {  
  62.         System.out.println("您办理的是股票交易第三方取款业务。" + "请到 " + s.getCounterNum() + " 窗口。");  
  63.     }  
  64.   
  65.     public void visit(SavingService s) {  
  66.         System.out.println("您办理的是股票交易第三方存款业务。" + "请到 " + s.getCounterNum() + " 窗口。");  
  67.     }  
  68. }  
  69. //  
  70. //基金买卖委托业务办理者  
  71. package designpattern.visitor.bank;  
  72. public class FundVisitor implements Visitor {  
  73.     public void visit(DrawService s) {  
  74.         System.out.println("您办理的是基金委托买入业务。" + "请到 " + s.getCounterNum() + " 窗口。");  
  75.     }  
  76.   
  77.     public void visit(SavingService s) {  
  78.         System.out.println("您办理的是基金委托卖出业务。" + "请到 " + s.getCounterNum() + " 窗口。");  
  79.     }  
  80. }  

优点:

对类结构增加新的操作很容易,只需要增加一个新的访问者。

缺点:

1 复杂

2 当数据结构或类发生变化的时候改动太大。

经典案例:

找ing。。。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值