访问者模式
这是23种设计模式的最后一个——访问者模式,这个模式确实不怎么好理解,不怎么好用,而且实际中也很少用到这个设计模式。《大话设计模式》中就提到GoF四个人中有一个说过:“大多数时候你并不需要访问者模式,但当一旦你需要访问者模式时,那就是真的需要它了。”我们先来说说访问者模式运用到什么场景中,它适用于数据结构相对稳定的系统中。什么什么数据结构相对稳定呢?比如我们下面要举得例子就能很少说明。
世界上只有男人和女人,他们对待事物的看法有所不同,男人、女人分别对成功、失败、恋爱等等的看法经常不同,我们把成功、失败抽象为一个状态分别有男人反应和女人反应两个方法,把男人、女人抽象成人。这时,男人和女人就是相对稳定的系统,我们将男人和女人对待事物看法不同用程序代码并且用访问者模式来表达。
说实话,光看这个对象结构都已经够复杂了,我们通过代码再来理解理解
首先是抽象的Action类即我们上面提到的状态抽象类,这里是接口。
1 package day_16_visitor; 2 3 /** 4 * 状态接口 5 * @author 余林丰 6 * 7 * 2016年10月16日 8 */ 9 public interface Action { 10 void getManConclusion(Man concreteEmelemtA); 11 void getWomanConclusion(Woman concreteEmeletB); 12 }
接着对是该接口的实现,我们实现两个类,“成功”和“失败”。
1 package day_16_visitor; 2 3 /** 4 * @author 余林丰 5 * 6 * 2016年10月16日 7 */ 8 public class Success implements Action { 9 10 /* (non-Javadoc) 11 * @see day_16_visitor.Action#getManConclusion(day_16_visitor.Man) 12 */ 13 @Override 14 public void getManConclusion(Man concreteEmelemtA) { 15 System.out.println("男人对成功的看法"); 16 } 17 18 /* (non-Javadoc) 19 * @see day_16_visitor.Action#getWomanConclusion(day_16_visitor.Woman) 20 */ 21 @Override 22 public void getWomanConclusion(Woman concreteEmeletB) { 23 System.out.println("女人对成功的看法"); 24 } 25 26 }
1 package day_16_visitor; 2 3 /** 4 * @author 余林丰 5 * 6 * 2016年10月16日 7 */ 8 public class Failure implements Action { 9 10 /* (non-Javadoc) 11 * @see day_16_visitor.Action#getManConclusion(day_16_visitor.Man) 12 */ 13 @Override 14 public void getManConclusion(Man concreteEmelemtA) { 15 System.out.println("男人对失败的看法"); 16 } 17 18 /* (non-Javadoc) 19 * @see day_16_visitor.Action#getWomanConclusion(day_16_visitor.Woman) 20 */ 21 @Override 22 public void getWomanConclusion(Woman concreteEmeletB) { 23 System.out.println("女人对失败的看法"); 24 } 25 26 }
接下来是“人”接口,这里是访问者模式的关键,在此种场景中只有男人和女人,所以这个分类相对来说是稳定的我们才能使用访问者模式。
1 package day_16_visitor; 2 3 /** 4 * 人 5 * @author 余林丰 6 * 7 * 2016年10月16日 8 */ 9 public interface Person { 10 void accept(Action visitor); //用来接收“状态”对象 11 }
1 package day_16_visitor; 2 3 /** 4 * @author 余林丰 5 * 6 * 2016年10月16日 7 */ 8 public class Man implements Person { 9 10 /* (non-Javadoc) 11 * @see day_16_visitor.Person#accept(day_16_visitor.Action) 12 */ 13 @Override 14 public void accept(Action visitor) { 15 visitor.getManConclusion(this); //这里用到了一个叫做双分派的技术。首先将“状态”对象传递给Man对象,这是完成了第一次分派,再将Man自己this作为参数传递回状态对象,这便完成了第二次分派 16 } 17 }
这里使用了一个叫做“双分派”的技术。
1 package day_16_visitor; 2 3 /** 4 * @author 余林丰 5 * 6 * 2016年10月16日 7 */ 8 public class Woman implements Person { 9 10 /* (non-Javadoc) 11 * @see day_16_visitor.Person#accept(day_16_visitor.Action) 12 */ 13 @Override 14 public void accept(Action visitor) { 15 visitor.getWomanConclusion(this); //同Man类,双分派技术 16 } 17 18 }
接下来是一个对象结构,该类能够枚举它的元素。
1 package day_16_visitor; 2 3 import java.util.ArrayList; 4 import java.util.List; 5 6 /** 7 * 对象结构 8 * @author 余林丰 9 * 10 * 2016年10月16日 11 */ 12 public class ObjectStructure { 13 private List<Person> elements = new ArrayList<Person>(); 14 15 public void attach(Person element){ 16 elements.add(element); 17 } 18 19 public void remove(Person element){ 20 elements.remove(element); 21 } 22 23 public void display(Action visitor){ 24 for (Person p : elements){ 25 p.accept(visitor); 26 } 27 } 28 }
客户端测试代码。
1 package day_16_visitor; 2 3 /** 4 * @author 余林丰 5 * 6 * 2016年10月16日 7 */ 8 public class Client { 9 10 /** 11 * @param args 12 */ 13 public static void main(String[] args) { 14 ObjectStructure o = new ObjectStructure(); 15 o.attach(new Man()); 16 o.attach(new Woman()); 17 //成功时的反应 18 Success s = new Success(); 19 o.display(s); 20 } 21 22 }
以上我们就完成了访问者模式,访问者模式确实是现实场景不常用的模式,只有用在相对稳定的分类才能尝试使用访问者模式,千万不要生搬硬套设计模式,这样只会得不偿失。
不积跬步,无以至千里;不积小流,无以成江海。