定义:封装一些作用于某种数据结构各元素的操作,它可以在不改变数据结构的前提下定义作用于这些元素的新操作。
使用场景
(1) 一个对象结构包含很多类对象,它们有不同的接口,而你想对这些对象实施一些依赖于其具体类的操作。
(2) 需要对一个对象结构中的对象进行很多不同且不相关的操作,而你想避免让这些操作“污染”这些对象。
看完访问者模式定义和使用场景感觉还是太抽象了。先继续往下看吧
访问者模式类图如下
Visitor 抽象访问者
抽象类或接口,声明访问者可以访问那些具体元素,Visit 方法的参数必须是具体的被访问者
ConcreteVisitor 具体访问者
实现每个由 Visitor 声明的操作,每个操作针对一个具体的被访问者,做相应的逻辑。
Element 抽象元素
接口或者抽象类,声明接受哪一类访问者,程序上是通过 accept 方法中的参数来定义的。
ConcreteElement 具体元素
实现 accept 方法,通常是 visitor.visit(this),进本上都形成了一种模式了。
ObjectStruture 结构对象
元素生产者,一般容纳在多个不同类,不同接口的容器,如 List,Set,Map 等,在项目中一般很少出现出这个角色。
看到这里依然是云里雾里,不要灰心继续往下看
上实例
一个奔驰 4S 店中,有销售经理、仓库经理,今天刚进货一批车
(1) 销售经理要给客户讲解车辆信息,销售经理关心的是 车名字、售价、以及车的一些基本参数。
(2) 仓库经理只需要把车辆放入仓库保管记录就行了,仓库经理关心的是 车名字、车长度、车宽度(计算仓库容量)
类比上方的访问者模式类图:
销售经理和仓库经理是具体访问者,访问的对象是进货中的所有车辆,车辆型号可能不同
奔驰车就是 抽象元素(被访问者),而具体每一个型号的车(奔驰迈巴赫S580、奔驰E级 E 260L 等) 是具体元素(被访问者)
看到这里应该明白访问者模式的用途了,车是个抽象类,而车有很多种类型,而关心车的人有很多,但是不同的人关心每一辆具体车的不同参数。
代码实现如下
Visitor 抽象访问者
// 抽象访问者
public interface IVisitor
{
// 访问 MaybachS580 具体类
void Visit(MaybachS580 maybachS580);
// 访问 BenzE260L 具体类
void Visit(BenzE260L benzE260L);
}
ConcreteVisitor 具体访问者
// 销售经理访问者:具体访问者
public class SalesManagerVisitor : IVisitor
{
public void Visit(MaybachS580 maybachS580)
{
string msg = string.Format("我是销售经理这个车是:{0}_价格:{1}万_参数:{2}n", maybachS580.Name, maybachS580.Price, maybachS580.Parameter);
Console.WriteLine(msg);
}
public void Visit(BenzE260L benzE260L)
{
string msg = string.Format("我是销售经理这个车是:{0}_价格:{1}万_参数:{2}n", benzE260L.Name, benzE260L.Price, benzE260L.Parameter);
Console.WriteLine(msg);
}
}
// 仓库经理访问者:具体访问者
public class WarehouseManagerVisitor : IVisitor
{
public void Visit(MaybachS580 maybachS580)
{
string msg = string.Format("我是仓库经理这个车是:{0}_长:{1}毫米_宽:{2}毫米n", maybachS580.Name, maybachS580.Length, maybachS580.Width);
Console.WriteLine(msg);
}
public void Visit(BenzE260L benzE260L)
{
string msg = string.Format("我是仓库经理这个车是:{0}_长:{1}毫米_宽:{2}毫米 n", benzE260L.Name, benzE260L.Length, benzE260L.Width);
Console.WriteLine(msg);
}
}
Element 抽象元素
// 抽象奔驰汽车:抽象被访问者
public abstract class Benz
{
// 车名
private string _name;
// 长
private float _length;
// 宽
private float _width;
// 价格
private float _price;
// 车辆参数
private string _parameter;
public Benz()
{
}
public string Name
{
get { return _name; }
set { _name = value; }
}
public float Length
{
get { return _length; }
set { _length = value; }
}
public float Width
{
get { return _width; }
set { _width = value; }
}
public float Price
{
get { return _price; }
set { _price = value; }
}
public string Parameter
{
get { return _parameter; }
set { _parameter = value; }
}
// 接收访问者
public abstract void Accept(IVisitor visitor);
}
ConcreteElement 具体元素
// 迈巴赫S580:具体被访问者
public class MaybachS580 : Benz
{
public MaybachS580()
{
Name = "迈巴赫S580";
Length = 5470;
Width = 1921;
Price = 230;
Parameter = "排量4L、涡轮增压、百公里加速5.0S、9档手自一体、8个汽缸";
}
public override void Accept(IVisitor visitor)
{
// 设置访问者,需要访问自己
visitor.Visit(this);
}
}
// 奔驰E级 E 260L:具体被访问者
public class BenzE260L : Benz
{
public BenzE260L()
{
Name = "奔驰E级 E 260L";
Length = 5078;
Width = 1860;
Price = 38;
Parameter = "排量2L、涡轮增压、百公里加速7.7S、9档手自一体、4个汽缸";
}
public override void Accept(IVisitor visitor)
{
// 设置访问者,需要访问自己
visitor.Visit(this);
}
}
ObjectStruture 结构对象
// 奔驰4S店
public class Benz4SShop
{
// 保存元素
private List benzList = new List();
public Benz4SShop()
{
// 为了方便测试,直接在构造函数中添加了元素
Add(new MaybachS580());
Add(new BenzE260L());
}
// 添加元素
public void Add(Benz benz)
{
benzList.Add(benz);
}
// 删除元素
public void Remove(Benz benz)
{
benzList.Remove(benz);
}
public void Info(IVisitor visitor)
{
foreach(var benz in benzList)
{
benz.Accept(visitor);
}
}
}
测试代码如下
public class Client
{
public Client()
{
// 创建 4S 店
Benz4SShop benz4SShop = new Benz4SShop();
// 创建销售经理
SalesManagerVisitor smVisitor = new SalesManagerVisitor();
// 将销售经理赋值给 4S 店
benz4SShop.Info(smVisitor);
// 创建仓库经理
WarehouseManagerVisitor wmVisitor = new WarehouseManagerVisitor();
// 将仓库经理赋值给 4S 店
benz4SShop.Info(wmVisitor);
}
}
测试结果如下
优点:
(1) 符合单一职责
(2) 扩展性好
(3) 灵活性高
缺点:
(1) 具体元素对访问者公开细节
(2) 具体元素变更困难,具体元素的增、删、改 都比较困难
(3) 违背了依赖倒置原则,访问者依赖的是具体的类,而不是抽象元素,抛弃了对接口的依赖,而直接依赖实现类,扩展比较难



