`

访问者模式进阶(四)

 
阅读更多
         经过思考后,发现上一篇介绍的用反射改造访问者模式中,抽象访问者与具体访问者之间的继承关系有不妥的地方。
1)抽象访问者与具体访问者的之间的继承并没有必然的关系
2)用抽象类代替接口并不是一种好办法,毕竟Java不支持多重继承
3)ReflectionVisitor更像是一个具体类,《Java与模式》里面说“只有在分类学的角度上有意义时,才使用继承,不要从工具类继承”

被访问者没有变化
尝试用组合组合/聚合的方式去代替继承。
具体元素的接口与实现类没有变化,依然是依赖ReflectionVisitor
public interface Person {     
     void accept(ReflectionVisitor visitor);
}

public class Man implements Person{

	public void accept(ReflectionVisitor visitor) {
		visitor.visit(this);
	}
}

public class Woman implements Person{

	public void accept(ReflectionVisitor visitor) {
		visitor.visit(this);
	}
}


标识接口Visitor
保留Visitor接口,访问者Success与Love从继承ReflectionVisitor变成实现Visitor接口
public interface Visitor {
      void visit(Man man);
      void visit(Woman woman);
}

//成功时Man与Woman的不同表现
public class Success implements Visitor{

	public void visit(Man man) {
		System.out.println("当男人成功时,背后多半有一个伟大的女人");
	}

	public void visit(Woman girl) {
		System.out.println("当女人成功时,背后大多有一个不成功的男人");
	}
}

//恋爱时Man与Woman的不同表现
public class Love implements Visitor{

	public void visit(Man man) {
		System.out.println("当男人恋爱时,凡事不懂也装懂");
	}

	public void visit(Woman girl) {
		System.out.println("当女人恋爱时,遇事懂也装不懂");
	}
}


改造后的ReflectionVisitor

把ReflectionVisitor抽象出来成为一个接口,而Visitor以委派的方式,作为ReflectionVisitor实现类中的一个实例变量。
public interface ReflectionVisitor {
	public void visit(Person person);
}


import java.lang.reflect.Method;

public class ReflectionVisitorImpl implements ReflectionVisitor{
	
	//用组合/聚合的方式代替继承
	private Visitor visitor;
	
	public ReflectionVisitorImpl(Visitor visitor){
		if(visitor == null){
			throw new NullPointerException("大哥,visitor不能为空啊");
		}
		this.visitor = visitor;
	}
	
	public void visit(Person person) {
		// 判断是否是null
		if (person == null) {
			throw new NullPointerException("Person为空那就没戏了");
		}
		// 组装class数组,即调用动态方法的时候参数的类型
		Class[] classes = new Class[] { person.getClass() };
		// 组装与class数组相对应的值
		Object[] objects = new Object[] { person };
		try {
			// 查找方法
			Method m = visitor.getClass().getMethod("visit", classes);
			// 调用该方法
			m.invoke(visitor, objects);
		} catch (NoSuchMethodException e) {
			// 没有找到相应的方法
			System.out.println("在"+getClass().getName()+"中,你没有实现" + person.getClass().getName() + "的visit方法");
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}


ObjectStructure没有变化,仍然依赖于ReflectioVisitor
修改客户端的测试代码
import java.util.*;

public class ObjectStructure {
    private List<Person> elements = new ArrayList<Person>();

    public void attach(Person element){
    	elements.add(element);
    }
    
    public void detach(Person element){
    	elements.remove(elements);
    }
    
    //遍历各种具体元素并执行他们的accept方法

    public void display(ReflectionVisitor visitor){
    	for(Person p:elements){
    		p.accept(visitor);
    	}
    }
}



public class Client {
      public static void main(String[] args) {
		ObjectStructure o = new ObjectStructure();  //依赖于ObjectStructure
		//实例化具体元素
		o.attach(new Man());  
		o.attach(new Woman());

		//当成功时不同元素的不同反映
		Visitor suceessVisitor = new Success();
		ReflectionVisitor success = new ReflectionVisitorImpl(suceessVisitor);          
		o.display(success);
		
		//当恋爱时的不同反映
		Visitor loveVisitor = new Success();
		ReflectionVisitor love = new ReflectionVisitorImpl(loveVisitor);         
		o.display(love);
		
	}
}

结果显示:
当男人成功时,背后多半有一个伟大的女人
当女人成功时,背后大多有一个不成功的男人
当男人成功时,背后多半有一个伟大的女人
当女人成功时,背后大多有一个不成功的男人


小结
上面保留了Visitor接口,是作为一种标识接口规范具体访问者。上面Visitor中保留Visit(Man m)与Visit(Woman w)方法是为了保留原具体元素的结构,强制要求新加入的访问者必须实现这两个抽象方法。因为现在Visitor现在退化成一个标识接口,仅仅起到ReflectionVisitor中visit方法参数的限制的作用。所以也可以把Visitor接口中两个visit方法注释掉,这样就更加灵活,所以选择实现哪一种具体元素的visit方法。具体怎样做可以根据需求而定。
标识接口Java语言中有一些很著名的应用,比如java.io.Serializable与java.rmi.Remote等,仅仅是表明实现它的类属于一个特定的类型。

现在给出改造后的类图:


现在具体元素对访问者的依赖转移到ReflectionVisitor上。客户端同时依赖于ObjectStructure,Visitor接口,ReflectionVisitor接口
  • 大小: 86.5 KB
分享到:
评论
1 楼 tao1992 2014-03-24  
写得很好。现在才看到

相关推荐

Global site tag (gtag.js) - Google Analytics