类关系图


类角色说明
访问者模式涉及到抽象访问者角色、具体访问者角色、抽象节点角色、具体节点角色、结构对象角色以及客户端角色。
· 抽象访问者(Visitor)角色:声明了一个或者多个访问操作,形成所有的具体元素角色必须实现的接口。
· 具体访问者(ConcreteVisitor)角色:实现抽象访问者角色所声明的接口,也就是抽象访问者所声明的各个访问操作。
· 抽象节点(Node)角色:声明一个接受操作,接受一个访问者对象作为一个参量。
· 具体节点(Node)角色:实现了抽象元素所规定的接受操作。
· 结构对象(ObiectStructure)角色:有如下的一些责任,可以遍历结构中的所有元素;如果需要,提供一个高层次的接口让访问者对象可以访问每一个元素;如果需要,可以设计成一个复合对象或者一个聚集,如列(List)或集合(Set)。

意图
实现通过统一的接口访问不同类型元素的操作,并且通过这个接口可以增加新的操作而不改变元素的类。

适用性
当很多对象的接口不同,而我们希望通过这些对象有依赖于具体对象的操作时,可以使用访问者模式。
访问者模式不应当在什么情况下使用。
倾斜的可扩展性
 访问者模式仅应当在被访问的类结构非常稳定的情况下使用。换言之,系统很少出现需要加入新节点的情况。如果出现需要加入新节点的情况,那么就必须在每一个访问对象里加入一个对应于这个新节点的访问操作,而这是对一个系统的大规模修改,因而是违背"开一闭"原则的。
访问者模式允许在节点中加入新的方法,相应的仅仅需要在一个新的访问者类中加入此方法,而不需要在每一个访问者类中都加入此方法。
 显然,访问者模式提供了倾斜的可扩展性设计:方法集合的可扩展性和类集合的不可扩展性。换言之,如果系统的数据结构是频繁变化的,则不适合使用访问者模式。
"开一闭"原则和对变化的封装
 面向对象的设计原则中最重要的便是所谓的"开一闭"原则。一个软件系统的设计应当尽量做到对扩展开放,对修改关闭。达到这个原则的途径就是遵循"对变化的封装"的原则。这个原则讲的是在进行软件系统的设计时,应当设法找出一个软件系统中会变化的部分,将之封装起来。
 很多系统可以按照算法和数据结构分开,也就是说一些对象含有算法,而另一些对象含有数据,接受算法的操作。如果这样的系统有比较稳定的数据结构,又有易于变化的算法的话,使用访问者模式就是比较合适的,因为访问者模式使得算法操作的增加变得容易。
 反过来,如果这样一个系统的数据结构对象易于变化,经常要有新的数据对象增加进来的话,就不适合使用访问者模式。因为在访问者模式中增加新的节点很困难,要涉及到在抽象访问者和所有的具体访问者中增加新的方法。

泡妞的例子
情人节到了,要给每个MM送一束鲜花和一张卡片,可是每个MM送的花都要针对她个人的特点,每张卡片也要根据个人的特点来挑,我一个人哪搞得清楚,还是找花店老板和礼品店老板做一下Visitor,让花店老板根据MM的特点选一束花 ,让礼品店老板也根据每个人特点选一张卡,这样就轻松多了

泡妞的代码
using System;
using
 System.Collections;
//抽象节点(Node)角色:声明一个接受操作,接受一个访问者对象作为一个参量。

public abstract class Guest
{
    
protected string
 color;
    
public string
 Color
    
{
        
get{return color;}

        
set{color=value;}
    }

    
protected string character;
    
public string
 Character
    
{
        
get{return character;}

        
set{character=value;}
    }

    
public abstract void PickGift(Boss Boss);
}

//具体节点(Node)角色:实现了抽象元素所规定的接受操作。 
public class Girl:Guest
{
    
public Girl(string Color,string
 Character)
    
{
        
this.Color=
Color;
        
this.Character=
Character;
    }

    
//Methods访问者模式关键代码:节点接受访问者访问自己,将本身this作为参数传递
    public override void PickGift(Boss Boss)
    
{
        Boss.Pick(
this
);
    }

}

//抽象访问者(Visitor)角色:声明了一个或者多个访问操作,形成所有的具体元素角色必须实现的接口。
public abstract class Boss
{
    
public abstract void
 Pick(Guest guest);
}

//    具体访问者(ConcreteVisitor)角色:实现抽象访问者角色所声明的接口,也就是抽象访问者所声明的各个访问操作。
public class FlowerBoss:Boss
{
    
public override void
 Pick(Guest guest)
    
{
        
string flower=
PickFlower(guest.Color);
        Console.WriteLine(
"FlowerBoss:既然这个女孩喜欢"+guest.Color+"色,我想她一定喜欢"+flower+"!"
);
    }

    
private string PickFlower(string Color)
    
{
        
string flower=""
;
        
switch
(Color)
        
{
            
case "红色"
:
                flower
="红玫瑰"
;
                
break
;
            
case "黄色"
:
                flower
="黄玫瑰"
;
                
break
;
            
case "白色"
:
                flower
="白玫瑰"
;
                
break
;
        }

        
return flower;
    }

}

//    具体访问者(ConcreteVisitor)角色:实现抽象访问者角色所声明的接口,也就是抽象访问者所声明的各个访问操作。
public class CardBoss:Boss
{
    
public override void
 Pick(Guest guest)
    
{
        
string card=
PickCard(guest.Character);
        Console.WriteLine(
"CardBoss:既然这个女孩性格比较"+guest.Character+",我想她一定喜欢"+card+"!"
);
    }

    
private string PickCard(string Character)
    
{
        
string card=""
;
        
switch
(Character)
        
{
            
case "可爱"
:
                card
="樱桃小丸子图案的卡片"
;
                
break
;
            
case "文静"
:
                card
="用竹片制成的卡片"
;
                
break
;
        }

        
return card;
    }

}

//结构对象(ObiectStructure)角色
public class Guests
  
{
   
// Fields

   private ArrayList guests = new ArrayList();
 
   
// Methods

   public void Attach( Guest guest )
    
{
     guests.Add( guest );
   }

 
   
public void Detach( Guest guest )
    
{
     guests.Remove( guest );
   }

 
   
public void Accept( Boss boos )    //访问者模式关键代码:让集合中所有节点接收visitor访问
    {
     
foreach( Guest guest in
 guests )
       guest.PickGift( boos );
   }

 }

public class MyClass
{
    
public static void
 Main()
    
{
        Boss cardboss
=new
 CardBoss();
        Boss flowerboss
=new
 FlowerBoss();
        
        Girl Lucy
=new Girl("黄色","文静"
);
        Girl Lily
=new Girl("红色","可爱"
);
        Guests guests
=new
 Guests();
        guests.Attach(Lucy);
        guests.Attach(Lily);
        
        guests.Accept(cardboss);
        guests.Accept(flowerboss);
        RL();
    }

    
    
Helper methods
}


访问者模式
访问者模式的目的是封装一些施加于某种数据结构元素之上的操作。一旦这些操作需要修改的话,接受这个操作的数据结构可以保持不变。访问者模式适用于数据结构相对未定的系统,它把数据结构和作用于结构上的操作之间的耦合解脱开,使得操作集合可以 相对自由的演化。访问者模式使得增加新的操作变的很容易,就是增加一个新的访问者类。访问者模式将有关的行为集中到一个访问者对象中,而不是分散到一个个的节点类中。当使用访问者模式时,要将尽可能多的对象浏览逻辑放在访问者类中,而不是放到它的子类中。 访问者模式可以跨过几个类的等级结构访问属于不同的等级结构的成员类。

备注
访问者模式有如下的优点:
1. 访问者模式使得增加新的操作变得很容易。如果一些操作依赖于一个复杂的结构对象的话,那么一般而言,增加新的操作会很复杂。而使用访问者模式,增加新的操作就意味着增加一个新的访问者类,因此,变得很容易。
2. 访问者模式将有关的行为集中到一个访问者对象中,而不是分散到一个个的节点类中。
3. 访问者模式可以跨过几个类的等级结构访问属于不同的等级结构的成员类。迭代子只能访问属于同一个类型等级结构的成员对象,而不能访问属于不同等级结构的对象。访问者模式可以做到这一点。
4. 积累状态。每一个单独的访问者对象都集中了相关的行为,从而也就可以在访问的过程中将执行操作的状态积累在自己内部,而不是分散到很多的节点对象中。这是有益于系统维护的优点。

访问者模式有如下的缺点:
1. 增加新的节点类变得很困难。每增加一个新的节点都意味着要在抽象访问者角色中增加一个新的抽象操作,并在每一个具体访问者类中增加相应的具体操作。
2. 破坏封装。访问者模式要求访问者对象访问并调用每一个节点对象的操作,这隐含了一个对所有节点对象的要求:它们必须暴露一些自己的操作和内部状态。不然,访问者的访问就变得没有意义。由于访问者对象自己会积累访问操作所需的状态,从而使这些状态不再存储在节点对象中,这也是破坏封装的。

posted on 2007-08-05 14:32  十分之七  阅读(284)  评论(0)    收藏  举报