一、什么是访问者模式
数据结构与数据算法分离
一组固定的数据结构,具备不同的数据状态,将状态封装多继承,用数据状态去访问数据结构,使用双分发,做出反应.数据结构是固定的,数据算法是多变的,可以灵活增加算法.稳定的数据结构和易变的操作耦合问题。
UML图
Element:元素抽象类,定义一个接受访问类的接口
ConcreteElementA:具体元素,实现接受访问类接口,分选对应的访问操作.
Visitor:访问者,定义所有元素的操作接口,一般元素不会变动
ConcreteVisitorA: 具体的访问者,对不同元素实现不同的操作,以备分选
ObjectStrctre:元素聚合类,收集所有实例元素,统一执行元素的接受访问的操作
二、适用场景
对象结构比较稳定,但经常需要在此对象结构上定义新的操作。
需要对一个对象结构中的对象进行很多不同的并且不相关的操作,而需要避免这些操作“污染”这些对象的类,也不希望在增加新操作时修改这些类,像男人女人在同一情景下的不同反应.
三、优缺点
优点
符合单一职责原则
优秀的扩展性,灵活性
增加新的操作很容易,只需要增加新的访问者
缺点
增加新的数据结构很困难
不符合迪米特法则
不符合依赖倒置原则,依赖了具体,没有依赖抽象
四、大话中的例子
列举了男人女人在不同状态下的反应,一个状态抽象类,提供男人和女人的行为接口,具体的状态类,比如成功状态类里面就具体实现男人女人接口的内容,然后一个人类提供一个接受状态的接口, 男人继承人类,实现这个接口,根据传进来的状态类选择对应的行为,女人也一样.最后一个对象结构类,可以添加对象,删除对象, 展示所有对象的行为.
五、我的例子
using System;
using System.Collections.Generic;
namespace VisitorMode
{
class Program
{
static void Main(string[] args)
{
ObjectSanin sanins = new ObjectSanin();
sanins.Add(new Tsunade());
sanins.Add(new Jiraiya());
sanins.Add(new OrochImaru());
SaninAction saninAction = new Catchindg();
sanins.ShowAction(saninAction);
saninAction = new Talents();
sanins.ShowAction(saninAction);
Console.ReadKey();
}
}
/// <summary>
/// 木叶三忍
/// </summary>
abstract class KinoKanoSanin
{
public abstract void Accept(SaninAction saninAction);
}
/// <summary>
/// 大蛇丸
/// </summary>
class OrochImaru : KinoKanoSanin
{
public override void Accept(SaninAction saninAction)
{
saninAction.OrochimaruAction(this);
}
}
/// <summary>
/// 纲手
/// </summary>
class Tsunade : KinoKanoSanin
{
public override void Accept(SaninAction saninAction)
{
saninAction.TsunadeAction(this);
}
}
/// <summary>
/// 自来也
/// </summary>
class Jiraiya : KinoKanoSanin
{
public override void Accept(SaninAction saninAction)
{
saninAction.JiraiyaAction(this);
}
}
class ObjectSanin
{
List<KinoKanoSanin> sanins = new List<KinoKanoSanin>();
public void Add(KinoKanoSanin sanin)
{
sanins.Add(sanin);
}
public void ReMove(KinoKanoSanin sanin)
{
sanins.Remove(sanin);
}
public void ShowAction(SaninAction action)
{
foreach (var item in sanins)
{
item.Accept(action);
}
}
}
abstract class SaninAction
{
/// <summary>
/// 大蛇丸行为
/// </summary>
public abstract void OrochimaruAction(OrochImaru orochImaru);
/// <summary>
/// 纲手行为
/// </summary>
public abstract void TsunadeAction(Tsunade tsunade);
/// <summary>
/// 自来也行为
/// </summary>
public abstract void JiraiyaAction(Jiraiya jiraiya);
}
/// <summary>
/// 通灵之术
/// </summary>
class Catchindg : SaninAction
{
public override void JiraiyaAction(Jiraiya jiraiya)
{
Console.WriteLine("{0}:通灵之术!!!,召唤出{1}", jiraiya.GetType().Name, "哈蟆油太");
}
public override void OrochimaruAction(OrochImaru orochImaru)
{
Console.WriteLine("{0}:通灵之术!!!,召唤出{1}", orochImaru.GetType().Name, "万蛇");
}
public override void TsunadeAction(Tsunade tsunade)
{
Console.WriteLine("{0}:通灵之术!!!,召唤出{1}", tsunade.GetType().Name, "活蝓");
}
}
/// <summary>
/// 绝技
/// </summary>
class Talents : SaninAction
{
public override void JiraiyaAction(Jiraiya jiraiya)
{
Console.WriteLine("{0}:{1}", jiraiya.GetType().Name, "仙法");
}
public override void OrochimaruAction(OrochImaru orochImaru)
{
Console.WriteLine("{0}:{1}", orochImaru.GetType().Name, "转生之术");
}
public override void TsunadeAction(Tsunade tsunade)
{
Console.WriteLine("{0}:{1}", tsunade.GetType().Name, "百豪之术");
}
}
}
运行结果
PS:访问者模式一般不会去用,如果用的话那就真的在合适不过了。,所谓双分派是指不管类怎么变化,我们都能找到期望的方法运行。双分派意味着得到执行的操作取决于请求的种类和两个接收者的类型。