一.访问者模式的定义:
表示一个作用于某对象结构中的各元素的操作。它使你可以在不改变各元素类的前提下定义作用于这些元素的新操作。
二.访问者模式的结构和角色:
1.Visitor 抽象访问者角色,为该对象结构中具体元素角色声明一个访问操作接口。该操作接口的名字和参数标识了发送访问请求给具体访问者的具体元素角色,这样访问者就可以通过该元素角色的特定接口直接访问它。
2.ConcreteVisitor.具体访问者角色,实现Visitor声明的接口。
3.Element 定义一个接受访问操作(accept()),它以一个访问者(Visitor)作为参数。
4.ConcreteElement 具体元素,实现了抽象元素(Element)所定义的接受操作接口。
5.ObjectStructure 结构对象角色,这是使用访问者模式必备的角色。它具备以下特性:能枚举它的元素;可以提供一个高层接口以允许访问者访问它的元素;如有需要,可以设计成一个复合对象或者一个聚集(如一个列表或无序集合)。
三.访问者模式的实现:
在生活中,我们要去医院看病,当我们挂号找到主治医生的医生看完病以后,会给你开一张药单,这个时候你需要先去付费,由划价医生收取药费,然后你拿着付费单再去找抓药师抓药。传统上我们也许会,写一个划价师类,里面根据药的名称做判断收取多少费用;写一个抓药师根据药单上面名称要判断去哪里抓药。可能使用Switch进行判断,这样增加了药就要改变划价人员和药房工作者的代码。
访问者模式的目的是封装一些施加于某种数据结构元素之上的操作,一旦这些操作需要修改的话,接受这个操作的数据结构可以保持不变。为不同类型的元素提供多种访问操作方式,且可以在不修改原有系统的情况下增加新的操作方式,这就是访问者模式的模式动机。
/// <summary>
/// 抽象访问者
/// </summary>
public abstract class Visitor
{
protected string name { get; set; } public Visitor(string name)
{
this.name = name;
} public abstract void visitor(MedicineA a); public abstract void visitor(MedicineB b);
} // 何问起 hovertree.com
/// <summary>
/// 具体访问者:划价员
/// </summary>
public class Charger :Visitor
{
public Charger(string name) : base(name) { } public override void visitor(MedicineA a)
{
Console.WriteLine("划价员:"+this.name+"给药"+a.GetName()+"价格:"+a.GetPrice());
} public override void visitor(MedicineB b)
{
Console.WriteLine("划价员:" + this.name + "给药" + b.GetName() + "价格:" + b.GetPrice());
}
} // 何问起 hovertree.com
/// <summary>
/// 具体访问者:药房工作者
/// </summary>
public class WorkerOfPharmacy:Visitor
{
public WorkerOfPharmacy(string name) : base(name) { }
public override void visitor(MedicineA a)
{
Console.WriteLine("药房工作者:"+this.name+",拿药:"+a.GetName());
} public override void visitor(MedicineB b)
{
Console.WriteLine("药房工作者:" + this.name + ",拿药:" + b.GetName());
}
} // 何问起 hovertree.com
/// <summary>
/// 抽象元素:药
/// </summary>
public abstract class Medicine
{
protected string name { get; set; }
protected double price { get; set; } public Medicine(string name, double price)
{
this.name = name;
this.price = price;
} public string GetName()
{
return name;
} public double GetPrice()
{
return price;
} public void SetPrice(double price)
{
this.price = price;
} public abstract void accept(Visitor visitor);
} // 何问起 hovertree.com
/// <summary>
/// 具体元素:A名称药
/// </summary>
public class MedicineA:Medicine
{
public MedicineA(string name, double price) : base(name, price) { } public override void accept(Visitor visitor)
{
visitor.visitor(this);
}
} // 何问起 hovertree.com
/// <summary>
/// 具体元素:B名称药
/// </summary>
public class MedicineB:Medicine
{
public MedicineB(string name, double price) : base(name, price) { } public override void accept(Visitor visitor)
{
visitor.visitor(this);
}
} // 何问起 hovertree.com
/// <summary>
/// 具体元素:药单
/// </summary>
public class Presciption
{
private List<Medicine> listmedicine = new List<Medicine>();
public void accpet(Visitor visitor)
{
foreach (var item in listmedicine)
{
item.accept(visitor);
}
} public void add(Medicine med)
{
listmedicine.Add(med);
} public void remove(Medicine med)
{
listmedicine.Remove(med);
}
} // 何问起 hovertree.com
/// <summary>
/// C#设计模式-访问者模式
/// </summary>
class Program
{
static void Main(string[] args)
{
//药类型
Medicine a = new MedicineA("药A", );
MedicineB b = new MedicineB("药B", ); //药单
Presciption presciption = new Presciption();
presciption.add(a);
presciption.add(b); Visitor charger = new Charger("张三"); //划价员
Visitor workerOfPharmacy = new WorkerOfPharmacy("李四"); //抓药员 presciption.accpet(charger); //划价
Console.WriteLine();
presciption.accpet(workerOfPharmacy); //抓药
}
} // 何问起 hovertree.com
四.访问者模式优缺点:
优点:
1、使得新增新的访问操作变得更加简单。
2、能够使得用户在不修改现有类的层次结构下,定义该类层次结构的操作。
3、将有关元素对象的访问行为集中到一个访问者对象中,而不是分散搞一个个的元素类中。
缺点:
1、增加新的元素类很困难。在访问者模式中,每增加一个新的元素类都意味着要在抽象访问者角色中增加一个新的抽象操作,并在每一个具体访问者类中增加相应的具体操作,违背了“开闭原则”的要求。
2、破坏封装。当采用访问者模式的时候,就会打破组合类的封装。
3、比较难理解
五.使用场景:
1、对象结构中对象对应的类很少改变,但经常需要在此对象结构上定义新的操作。
2、需要对一个对象结构中的对象进行很多不同的并且不相关的操作,而需要避免让这些操作“污染”这些对象的类,也不希望在增加新操作时修改这些类。