论坛首页 Java企业应用论坛

访问者(Visitor)模式【行为模式第六篇】

浏览 1616 次
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
作者 正文
   发表时间:2009-10-23   最后修改:2010-10-20
访问者(Visitor)模式:
访问者模式是对象的行为模式。访问者模式的目的是封装一些施加于某种数据结构元素之上的操作。一旦这些操作要修改的话,接受这个操作的数据
结构则 可以保持不变。


聚集是的大多数的系统都要处理一种容器对象,它保存了对其他对象的引用。相信大多数读者都有处理聚集的经验,但是人家处理过的大多数聚集
恐怕 都是同类操作,而迭代子模式就是为这种情况准备的设计模式。

如果需要针对一个包含不同类型元素的聚集采取某种操作,而操作的细节根据元素的类型不同而有所不同时,就会出现必须对元素类型做类型判断
的条件转移语句。

一、访问者模式涉及到抽象访问者角色、具体访问者角色、抽象节点角色、具体节点角色、结构对象角色以及客户端角色。
1、抽象访问者角色:声明了一个或者多个访问操作,形成所有的具体元素角色必须实现的接口。
2、具体访问者角色:实现抽象访问者角色所声明的接口,也就是抽象访问者所声明的各个访问操作。
3、抽象节点角色:声明一个接受操作,接受一个访问者对象作为一个参量。
4、具体节点角色:实现了抽象元素所规定的接受操作。
5、结构对象角色:有如下的一些责任,可以遍历结构中的所有元素;如果需要,提供一个高层次的接口让访问者对象可以访问每一个元素
   如果需要,可以设计成一个复合对象或者一个聚集。
//抽象访问者角色
		   public interface Visitor{
		   	//对于NodeA的访问操作
		   	void visit(NodeA node);
		   	
		   	//对于NodeB的访问操作
		   	void visit(NodeB node);
		   }
		   
		   //具体访问者角色
		   public class VisitorA implements Visitor{
		   	//对应于NodeA的访问操作
		   	public void visit(NodeA nodeA){
		   		System.out.println(nodeA.operationA());
		   	}
		   	
		   	//对应于NodeB的访问操作
		   	public void visit(NodeB nodeB){
		   		System.out.println(nodeB.operationB());
		   	}
		   }
		   
		    public class VisitorB implements Visitor{
		   	//对应于NodeA的访问操作
		   	public void visit(NodeA nodeA){
		   		System.out.println(nodeA.operationA());
		   	}
		   	
		   	//对应于NodeB的访问操作
		   	public void visit(NodeB nodeB){
		   		System.out.println(nodeB.operationB());
		   	}
		   }
		   
		   //抽象节点角色
		   public abstract class Node{
		   	//接受操作
		   	public abstract void accept(Visitor visitor);
		   }
		   
		   //模式中具体节点NodeA角色
		   public class NodeA extends Node{
		   	//接受操作
		   	public void accept(Visitor visitor){
		   		visitor.visit(this);
		   	}
		   	
		   	//NodeA特有的商业方法
		   	public String operationA(){
		   		return "NodeA is visited.";
		   	}
		   }
		   
		    public class NodeB extends Node{
		   	//接受操作
		   	public void accept(Visitor visitor){
		   		visitor.visit(this);
		   	}
		   	
		   	//NodeA特有的商业方法
		   	public String operationB(){
		   		return "NodeB is visited.";
		   	}
		   }
		   
		   //模式中结构对象ObjectStructure
		   import java.util.Vector;
		   import java.util.Enumeration;
		   public class ObjectStructure{
		   	private Vector nodes;
		   	private Node node;
		   	public ObjectStructure(){
		   		nodes = new Vector();
		   	}
		   	
		   	//执行访问操作
		   	public void action(Visitor visitor){
		   		for(Enumeration e = nodes.elements(); e.hasMoreElements();){
		   			node = (Node)e.nextElement();
		   			node.accept(visitor);
		   		}
		   	}
		   	
		   	//增加一个新的元素
		   	public void add(Node node){
		   		nodes.addElements(node);
		   	}
		   }
		   
		   //客户端
		   public class Client{
		   	private static ObjectStructure aObjects;
		   	private static Visitor visitor;
		   	
		   	public static void main(String args[]){
		   		//创建一个结构对象
		   		aObjects =  new ObjectStruture();
		   		
		   		//给结构增加一个节点
		   		aObjects.add(new NodeA());
		   		
		   		//给结构增加一个节点
		   		aObjects.add(new NodeB());
		   		
		   		//创建一个新的访问者
		   		visitor = new VisitorA();
		   		
		   		//让访问者访问结构
		   		aObjects.action(visitor);
		   	}
		   }


二、访问者模式仅应当在被访问的类结构非常稳定的情况下使用。换言之,系统很少出现需要加入新节点的情况。如果出现需要加入的新节点
    这时就必须在每一个访问对象里加入一个对应于这个新节点的访问操作,而这是对一个系统的大规模修改,因而是违背“开闭”原则的。
   
    访问这模式允许在节点中加入新的方法,相应的仅仅需要在一个新的访问者类中加入此方法,而不需要在每一个访问者类中都加入此方法。
    显然,访问者模式提供了倾斜的可扩展性设计:方法集合的可扩展性和类集合的不可扩展性。
   
    换言之,如果系统的数据结构是濒繁变化的,则不适合使用访问者模式。
   
三、使用访问者模式的优缺点:
1、优点:
(1)访问者模式使得增加新的操作变得很容易。如果一些操作依赖于一个复杂的结构对象的话,那么一般而言,增加
     新的操作会很复杂。而使用访问者模式,增加新的操作己意味这增加一个新的访问者类,因此,变得很容易。

(2)访问者模式将有关的行为几种到一个访问者对象中,而不是分散到一个个的节点类中。

(3)访问者模式可以跨过几个类的等级结构访问属于不同的等级结构的成员类。迭代子只能访问属于同一个类型等级结构
     的成员对象,而不能访问属于不同等级结构的对象。访问者模式可以做到这一点。
    
(4)积累状态。每一个单独的访问者对象都集中了相关的行为,从而也就可以在访问的过程中将执行操作的状态积累在自己
     内部,而不是分散到很多的节点对象中。这是有益于系统维护的优点。
    
2、缺点:
(1)增加新的节点类变得很困难。没增加一个新的节点都意味着要在抽象访问者角色中增加一个新的抽象操作,并在每一个
     具体访问者类中增加相应的具体操作。
 
(2)破坏封装。访问者模式要求访问者对象并调用每一个节点对象的操作,这隐含了一个对所有节点对象的要求:它们必须
     暴露一些自己的操作和内部状态,从而使这些状态不再存储在节点对象中,这也是破坏封装的。
    
当实现访问者模式时,要将尽可能多的对象浏览逻辑放在(抽象访问者)Visitor类中,而不是放在它的子类里。
这样的话,具体访问者类对所有的对象结构依赖较少,从而使维护较为容易。

负责遍历行为的,可供选择的有:结构对象、访问者对象或者创建一个新的迭代对象。
(1)由结构对象负责迭代是最常见的选择,这也就是需要结构对象角色的出发点。
(2)另一个解决方案是使用一个迭代对象来负责遍历行为。
(3)将遍历行为放到访问者中是第三个可能的选择。一般而言,这样做会导致每一个具体访问者都不得不具有管理聚集
     的内部功能,而这在一般情况下是不理想的。然而如果遍历的逻辑较为复杂的话,将所有的遍历逻辑方到结构对象
     角色中,不如这种做法也不失为一种可行的选择。

//一个电脑专卖系统
		//抽象访问者角色
		public abstract class Visitor{
			public abstract void visitHardDisk(HardDisk e);
			public abstract void visitMainBoard(MainBoard e);
			public abstract void visitCpu(Cpu e);
			public abstract void visitPc(Pc e);
			public abstract void visitCase(Case e);
			public abstract void visitIntegratedBoard(IntegratedBoard e);
		}
		
		//具体访问者角色
		public class PriceVisitor extends Visitor{
			private float total;
			
			public PriceVisitor(){
				total = 0;
			}
			
			public float value(){
				return total;
			}
			
			public void visitHardDisk(HardDisk e){
				total += e.price();
			}
			
			public void visitMainBoard(MainBoard e){
				total += e.price();
			}
			
			public void visitCpu(Cpu e){
				total += e.price();
			}
			
			public void visitPc(Pc e){
				total += e,price();
			}
			
			public void visitCase(Case e){
				total += e.price();
			}
			
			public void visitIntegratedBoard(IntegratedBoard e){
				total += e.price();
			}
		}
		
		//具体访问角色
		import java.util.Vector;
		public class InventoryVisitor extends Visitor{
			private Vector inv;
			
			public InventoryVisitor(){
				inv = new Vector(10.5);
			}
			
			public int size(){
				return inv.size();
			}
			
			public void visitHardDisk(HardDisk e){
				inv.addElement(e);
			}
			
			public void visitMainBoard(MainBoard e){
				inv.addElement(e);
			}
			
			public void visitCpu(Cpu e){
				inv.addElement(e);
			}
			
			public void visitPc(Pc e){
				inv.addElement(e);
			}
			
			public void visitCase(Case e){
				inv.addElement(e);
			}
			
			public void visitIntegratedBoard(IntegratedBoard e){
				inv.addElement(e);
			}
		}
		
		//抽象节点角色
		public abstract class Equipment{
			//接受方法
			public abstract void accept(Visitor vis);
			
			//商业方法
			public abstract double price();
		}
	
		//具体节点角色
		public class MainBoard extends Equipment{
			public double price(){
				return 100.00;
			}
			
			public void accept(Visitor v){
				System.out.println("MainBoard has been visited.");
				v.visitMainBoard(this);
			}
		}
		
		//具体节点角色
		public class HardDisk extends Equipment{
			public double price(){
				return 200.00;
			}
			
			public void accept(Visitor v){
				System.out.println("HardDisk has been visited.");
				v.visitHardDisk(this);
			}
		}
		
		//具体节点角色
		public class Cpu extends Equipment{
			public double price(){
				return 800.00;
			}
			
			public void accept(Visitor v){
				System.out.println("CPU has been visited.");
				v.visitCpu(this);
			}
		}
		
		//具体节点角色
		public class Case extends Equipment{
			public double price(){
				return 30.00;
			}
			
			public void accept(Visitor v){
				System.out.println("Case has been visited.");
				v.visitCase(this);
			}
		}
		
		//抽象复合节点
		public abstract class Composite extends Equipment{
			private Vector parts = new Vector(10);
			
			public Composite(){}
			
			public double price(){
				double total = 0;
				for(int i = 0; i < parts.size(); i ++){
					total += ((Equipment)parts.get(i)).price();
				}
				return total;
			}
			
			public void accept(Visitor v){
				for(int i = 0; i < parts.size(); i ++){
					((Equipment)parts.get(i)).accept(v);
				}
			}
		}
		 
		 //具体复合节点角色
		 public class IntegratedBoard extends Composite{
		 	public IntegratedBoard(){
		 		super.add(new MainBoard());
		 		super.add(new Cpu());
		 	}
		 	
		 	public void accept(Visitor v){
		 		System.out.println("IntegratedBoard has been visited.");
		 		super.accept(v);
		 	}
		 }
		 
		  //具体复合节点角色
		 public class Pc extends Composite{
		 	public Pc(){
		 		super.add(new IntegratedBoard());
		 		super.add(new HardDisk());
		 		super.add(new Case());
		 	}
		 	
		 	public void accept(Visitor v){
		 		System.out.println("Pc has been visited.");
		 		super.accept(v);
		 	}
		 }
		
		
		//客户端
		public class Client{
			private static PriceVisitor pv;
			private static InventoryVisitor iv;
			private static Equipment equip;
			
			public static void main(String args[]){
				equip = new Pc();
				pv = new PriceVisitor();
				equip.accept(pv);
				System.out.println("Price: " + pv.value());
				System.out.println();
				
				iv = new InventoryVisitor();
				equip.accept(iv);
				System.out.println("Number of parts: " + iv.size());
			}
		}
论坛首页 Java企业应用版

跳转论坛:
Global site tag (gtag.js) - Google Analytics