访问者模式(Visitor)
本质:预留通路,回调实现
访问者模式的目的是封装一些施加于某种数据结构元素之上的操作。一旦这些操作需要修改的话,接受这个操作的数据结构则可以保持不变。
结构对象会遍历它自己所保存的聚集中的所有节点,在本系统中就是节点ConcreteElementA和节点ConcreteElementB。首先ConcreteElementA会被访问到,这个访问是由以下的操作组成的:
- ConcreteElementA对象的Accept方法被调用,并将Visitor1对象本身传入;
- ConcreteElementA对象反过来调用Visitor1对象的访问方法VisitConcreteElementA,并将ConcreteElementA对象本身传入;
- 从而就完成了双重分派过程,接着,ConcreteElementB会被访问,这个访问的过程和ConcreteElementA被访问的过程是一样的。
具体访问者的数目与具体节点的数目没有任何联系
因此,结构对象对聚集元素的遍历过程就是对聚集中所有的节点进行委派的过程,也就是双重分派的过程。
访问者模式的目的是要把处理从数据结构分离出来
访问者模式仅应当在被访问的类结构非常稳定的情况下使用
换言之,系统很少出现需要加入新节点的情况。如果出现需要加入新节点的情况,那么就必须在每一个访问对象里加入一个对应于这个新节点的访问操作,而这是对一个系统的大规模修改,因而是违背“开一闭”原则的。
访问者模式提供了倾斜的可扩展性设计:方法集合的可扩展性和类集合的不可扩展性。换言之,如果系统的数据结构是频繁变化的,则不适合使用访问者模式。
示意性代码
class Program
{
static void Main(string[] args)
{
ObjectStructure o = new ObjectStructure();
o.Attach(new ConcreteElementA());
o.Attach(new ConcreteElementB());
ConcreteVisitor1 v1 = new ConcreteVisitor1();
ConcreteVisitor2 v2 = new ConcreteVisitor2();
o.Accept(v1);
o.Accept(v2);
Console.Read();
}
}
abstract class Visitor
{
public abstract void VisitConcreteElementA(ConcreteElementA concreteElementA);
public abstract void VisitConcreteElementB(ConcreteElementB concreteElementB);
}
class ConcreteVisitor1 : Visitor
{
public override void VisitConcreteElementA(ConcreteElementA concreteElementA)
{
Console.WriteLine("{0}被{1}访问", concreteElementA.GetType().Name, this.GetType().Name);
}
public override void VisitConcreteElementB(ConcreteElementB concreteElementB)
{
Console.WriteLine("{0}被{1}访问", concreteElementB.GetType().Name, this.GetType().Name);
}
}
class ConcreteVisitor2 : Visitor
{
public override void VisitConcreteElementA(ConcreteElementA concreteElementA)
{
Console.WriteLine("{0}被{1}访问", concreteElementA.GetType().Name, this.GetType().Name);
}
public override void VisitConcreteElementB(ConcreteElementB concreteElementB)
{
Console.WriteLine("{0}被{1}访问", concreteElementB.GetType().Name, this.GetType().Name);
}
}
abstract class Element
{
public abstract void Accept(Visitor visitor);
}
class ConcreteElementA : Element
{
public override void Accept(Visitor visitor)
{
visitor.VisitConcreteElementA(this);
}
public void OperationA()
{ }
}
class ConcreteElementB : Element
{
public override void Accept(Visitor visitor)
{
visitor.VisitConcreteElementB(this);
}
public void OperationB()
{ }
}
class ObjectStructure
{
private IList<Element> elements = new List<Element>();
public void Attach(Element element)
{
elements.Add(element);
}
public void Detach(Element element)
{
elements.Remove(element);
}
public void Accept(Visitor visitor)
{
foreach (Element e in elements)
{
e.Accept(visitor);
}
}
}
例子——男人女人
人类的种类只有男女两类,后期不会再增加或更改了,而行为可能会增加或更改
即状态类支持扩展,人类不支持拓展,且男人成功失败、女人成功失败之间是同构互传对象实现的,也就是所谓的双重分派。
class Program
{
static void Main(string[] args)
{
ObjectStructure o = new ObjectStructure();
o.Attach(new Man());
o.Attach(new Woman());
Success v1 = new Success();
o.Display(v1);
Failing v2 = new Failing();
o.Display(v2);
Amativeness v3 = new Amativeness();
o.Display(v3);
Marriage v4 = new Marriage();
o.Display(v4);
Console.Read();
}
}
//状态
abstract class Action
{
//得到男人结论或反应
public abstract void GetManConclusion(Man concreteElementA);
//得到女人结论或反应
public abstract void GetWomanConclusion(Woman concreteElementB);
}
//成功
class Success : Action
{
public override void GetManConclusion(Man concreteElementA)
{
Console.WriteLine("{0}{1}时,背后多半有一个伟大的女人。", concreteElementA.GetType().Name, this.GetType().Name);
}
public override void GetWomanConclusion(Woman concreteElementB)
{
Console.WriteLine("{0}{1}时,背后大多有一个不成功的男人。", concreteElementB.GetType().Name, this.GetType().Name);
}
}
//失败
class Failing : Action
{
public override void GetManConclusion(Man concreteElementA)
{
Console.WriteLine("{0}{1}时,闷头喝酒,谁也不用劝。", concreteElementA.GetType().Name, this.GetType().Name);
}
public override void GetWomanConclusion(Woman concreteElementB)
{
Console.WriteLine("{0}{1}时,眼泪汪汪,谁也劝不了。", concreteElementB.GetType().Name, this.GetType().Name);
}
}
//恋爱
class Amativeness : Action
{
public override void GetManConclusion(Man concreteElementA)
{
Console.WriteLine("{0}{1}时,凡事不懂也要装懂。", concreteElementA.GetType().Name, this.GetType().Name);
}
public override void GetWomanConclusion(Woman concreteElementB)
{
Console.WriteLine("{0}{1}时,遇事懂也装作不懂", concreteElementB.GetType().Name, this.GetType().Name);
}
}
//结婚
class Marriage : Action
{
public override void GetManConclusion(Man concreteElementA)
{
Console.WriteLine("{0}{1}时,感慨道:恋爱游戏终结时,‘有妻徒刑’遥无期。", concreteElementA.GetType().Name, this.GetType().Name);
}
public override void GetWomanConclusion(Woman concreteElementB)
{
Console.WriteLine("{0}{1}时,欣慰曰:爱情长跑路漫漫,婚姻保险保平安。", concreteElementB.GetType().Name, this.GetType().Name);
}
}
//人
abstract class Person
{
//接受
public abstract void Accept(Action visitor);
}
//男人
class Man : Person
{
public override void Accept(Action visitor)
{
visitor.GetManConclusion(this);
}
}
//女人
class Woman : Person
{
public override void Accept(Action visitor)
{
visitor.GetWomanConclusion(this);
}
}
//对象结构
class ObjectStructure
{
private IList<Person> elements = new List<Person>();
//增加
public void Attach(Person element)
{
elements.Add(element);
}
//移除
public void Detach(Person element)
{
elements.Remove(element);
}
//查看显示
public void Display(Action visitor)
{
foreach (Person e in elements)
{
e.Accept(visitor);
}
}
}
优缺点
优点:
- 访问者模式使得增加新的操作变得很容易。增加新的操作就意味着增加一个新的访问者类。
- 访问者模式将有关的行为集中到一个访问者对象中,而不是分散到一个个的节点类中。
缺点:
- 增加新的节点类变得很困难。每增加一个新的节点都意味着要在抽象访问者角色中增加一个新的抽象操作,并在每一个具体访问者类中增加相应的具体操作。
- 破坏封装。访问者模式要求访问者对象访问并调用每一个节点对象的操作,这隐含了一个对所有节点对象的要求:它们必须暴露一些自己的操作和内部状态。
利用访问者模式模拟实现拓展客户管理系统(CRM)
涉及客户:
个人客户、企业用户
涉及服务:
提出服务申请、客户价值分析、客户对产品的偏好分析
package visitor;
public abstract class Analysis {
public abstract void ManageIndividual(Individual i);
public abstract void ManageEnterprise(Enterprise e);
}
package visitor;
public class PreferenceAnalysis extends Analysis{
@Override
public void ManageIndividual(Individual i) {
// TODO Auto-generated method stub
System.out.println("个人客户"+i.getName()+"对产品偏好分析");
}
@Override
public void ManageEnterprise(Enterprise e) {
// TODO Auto-generated method stub
System.out.println("企业客户"+e.getName()+"对产品偏好分析");
}
}
package visitor;
public class ServiceApplication extends Analysis{
@Override
public void ManageIndividual(Individual i) {
// TODO Auto-generated method stub
System.out.println("个人客户"+i.getName()+"提出服务申请");
}
@Override
public void ManageEnterprise(Enterprise e) {
// TODO Auto-generated method stub
System.out.println("企业客户"+e.getName()+"提出服务申请");
}
}
package visitor;
public class ValueAnalysis extends Analysis{
@Override
public void ManageIndividual(Individual i) {
// TODO Auto-generated method stub
System.out.println("个人客户"+i.getName()+"客户价值分析");
}
@Override
public void ManageEnterprise(Enterprise e) {
// TODO Auto-generated method stub
System.out.println("企业客户"+e.getName()+"客户价值分析");
}
}
package visitor;
public abstract class Customer {
private String name;
public Customer(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public abstract void Operate(Analysis a);
}
package visitor;
public class Enterprise extends Customer {
public Enterprise(String name) {
super(name);
// TODO Auto-generated constructor stub
}
@Override
public void Operate(Analysis a) {
// TODO Auto-generated method stub
a.ManageEnterprise(this);
}
}
package visitor;
public class Individual extends Customer{
public Individual(String name) {
super(name);
// TODO Auto-generated constructor stub
}
@Override
public void Operate(Analysis a) {
// TODO Auto-generated method stub
a.ManageIndividual(this);
}
}
package visitor;
import java.util.ArrayList;
public class ObjectStructure {
private ArrayList<Customer> cl = new ArrayList<Customer>();
public void Attach(Customer c) {
cl.add(c);
}
public void Detach(Customer c) {
cl.remove(c);
}
public void Display(Analysis a) {
for (Customer c : cl)
c.Operate(a);
}
}
package visitor;
public class Main {
public static void main(String args[]) {
ObjectStructure os = new ObjectStructure();
os.Attach(new Individual("CustomerA"));
os.Attach(new Enterprise("CompanyB"));
Analysis a1 = new ValueAnalysis();
os.Display(a1);
Analysis a2 = new PreferenceAnalysis();
os.Display(a2);
Analysis a3 = new ServiceApplication();
os.Display(a3);
}
}
UML图