场景:长途汽车票、火车票价格调整
描述:春运开始了,人流高峰期,打工的人群要回家过年了,票价要涨了!
1、抽象访问者(Visitor,程序中为:NotifyVisitor)角色:声明了一个或者多个访问操作,形成所有的具体元素角色必须实现的接口。
2、具体访问者(ConcreteVisitor,程序中为各个期间的票价浮动情况)角色:实现抽象访问者角色所声明的接口,也就是抽象访问者所声明的各个访问操作。
3、抽象节点(Element,程序中的交通工具基类)角色:声明一个接受操作,接受一个访问者对象作为一个参量。
4、具体节点(ConcreteElement,程序中的各类交通工具)角色:实现了抽象元素所规定的接受操作。
5、结构对象(ObiectStructure,程序中的交通管理部门)角色:有如下的一些责任,可以遍历结构中的所有元素;如果需要,提供一个高层次的接口让访问者对象可以访问每一个元素;如果需要,可以设计成一个复合对象或者一个聚集,如列(List)或集合(Set)。
在软件构建过程中,由于需求的改变,某些类层次结构中常常需要增加新的行为(方法),如果直接在基类中做这样的改变,将会给子类带来很繁重的变更负担,甚至破坏原有设计。
如何在不更改类层次结构的前提下,在运行时根据需要透明的为类层次结构上的各个类动态添加新的操作,从而避免上述问题?
标识一个作用于某对象结构中的各元素的操作。它可以在不改变各元素的类的前提下定义作用于这些元素的新的操作。
实现1:在不使用访问者的时候
(一)交通工具标准
//交通工具
public abstract class Vehicle
{
public abstract void ShowPrice();
//过年涨价
public abstract void PriceFloatup();
}
(二)长途汽车与火车
//bus
public class Bus : Vehicle
{
public override void ShowPrice()
{
Console.WriteLine("长途汽车石家庄到邢台50元");
}
public override void PriceFloatup()
{
Console.WriteLine("春运开始了,汽车票在原价的基础上,上浮20%!");
}
}
//train
public class Train : Vehicle
{
public override void ShowPrice()
{
Console.WriteLine("火车石家庄到邢台40元");
}
public override void PriceFloatup()
{
Console.WriteLine("春运开始了,火车票在原价的基础上,上浮15%!");
}
}
(三)测试
Bus bus = new Bus();
bus.ShowPrice();
bus.PriceFloatup();
Train train = new Train();
train.ShowPrice();
train.PriceFloatup();
结果:
长途汽车石家庄到邢台50元
春运开始了,汽车票在原价的基础上,上浮20%!
火车石家庄到邢台40元
春运开始了,火车票在原价的基础上,上浮15%!
(四)特殊的日子,涨价
国庆期间,票价也要涨!
现在要在长途与火车中添加新的涨价通知。那么在基类中添加接口:
//国庆涨价
public abstract void PriceNationdayFloatup();
在两个实现中添加实现:
public override void PriceNationdayFloatup()
{
Console.WriteLine("国庆期间,汽车票在原价的基础上,上浮5%!");
}
……
public override void PriceNationdayFloatup()
{
Console.WriteLine("国庆期间,火车票在原价的基础上,上浮3%!");
}
(五)测试
Bus bus = new Bus();
bus.ShowPrice();
bus.PriceNationdayFloatup();
Train train = new Train();
train.ShowPrice();
train.PriceNationdayFloatup();
結果:
长途汽车石家庄到邢台50元
国庆期间,汽车票在原价的基础上,上浮5%!
火车石家庄到邢台40元
国庆期间,火车票在原价的基础上,上浮3%!
如果将来国家生产力高度发达,票价下调,那么,还要在两个类实现的基础上添加各自的通知方法。
现在以访问者来实现整个通知系统。
(一)访问者抽象
涨价总是变,在不同的时期,总是要涨价。所以价格因素这里为访问者。
//上面的通知,涨价
public abstract class NotifyVisitor
{
public abstract void Visit(Bus bus);
public abstract void Visit(Train train);
}
(二)客运部门
长途汽车,火车部门接受访问者的通知。
//交通工具
public abstract class Vehicle
{
public abstract void ShowPrice();
//过年涨价
public abstract void Accept(NotifyVisitor visitor);
}
(三) 客运部门实现
//bus
public class Bus : Vehicle
{
public override void ShowPrice()
{
Console.WriteLine("长途汽车石家庄到邢台50元");
}
public override void Accept(NotifyVisitor visitor)
{
visitor.Visit(this);
}
}
//train
public class Train : Vehicle
{
public override void ShowPrice()
{
Console.WriteLine("火车石家庄到邢台40元");
}
public override void Accept(NotifyVisitor visitor)
{
visitor.Visit(this);
}
}
(四)春运要涨价
这是一个实现的访问者
public class NewYearVisitor : NotifyVisitor
{
public override void Visit(Bus bus)
{
bus.ShowPrice();
Console.WriteLine("春运开始了,汽车票在原价的基础上,上浮20%!");
}
public override void Visit(Train train)
{
train.ShowPrice();
Console.WriteLine("春运开始了,火车票在原价的基础上,上浮15%!");
}
}
它的目的就是通知两个部门,要涨价及涨价的细节。
(五)交通管理部门
用于确定要涨价的部门。这是可分配的:交通工具有很多种,长途汽车与火车是其中的两种,这次是两者都要涨!
public class TraffiMnagement
{
IList<Vehicle> _list = new List<Vehicle>();
public void Add(Vehicle vehicle)
{
_list.Add(vehicle);
}
public void Detach(Vehicle vehicle)
{
_list.Remove(vehicle);
}
public void Accept(NotifyVisitor visitor)
{
foreach (Vehicle vv in _list)
{
vv.Accept(visitor);
}
}
}
(六)测试
public void TestVisitor()
{
TraffiMnagement department = new TraffiMnagement();
department.Add(new Bus());
department.Add(new Train());
department.Accept(new NewYearVisitor());
}
结果:
长途汽车石家庄到邢台50元
春运开始了,汽车票在原价的基础上,上浮20%!
火车石家庄到邢台40元
春运开始了,火车票在原价的基础上,上浮15%!
(七) 国庆期间涨价
新加国庆访问者,其它的不用变动。
public class NationalDayNotifyVisitor : NotifyVisitor
{
public override void Visit(Bus bus)
{
bus.ShowPrice();
Console.WriteLine("国庆节期间,汽车票在原价的基础上,上浮5%!");
}
public override void Visit(Train train)
{
train.ShowPrice();
Console.WriteLine("国庆节期间,火车票在原价的基础上,上浮3%!");
}
}
(八)测试
public void TestVisitor()
{
TraffiMnagement department = new TraffiMnagement();
department.Add(new Bus());
department.Add(new Train());
department.Accept(new NationalDayNotifyVisitor());
}
结果:
长途汽车石家庄到邢台50元
国庆节期间,汽车票在原价的基础上,上浮5%!
火车石家庄到邢台40元
国庆节期间,火车票在原价的基础上,上浮3%!
(九)生产力发达了,票价要降了!
新加 愿望访问者。
public class WillVisitor : NotifyVisitor
{
public override void Visit(Bus bus)
{
bus.ShowPrice();
Console.WriteLine("生产力发达了人民幸福了,汽车票在原价的基础上,下调90%!");
}
public override void Visit(Train train)
{
train.ShowPrice();
Console.WriteLine("生产力发达了人民幸福了,火车票在原价的基础上,下调90%!");
}
}
(十)测试
public void TestVisitor()
{
TraffiMnagement department = new TraffiMnagement();
department.Add(new Bus());
department.Add(new Train());
department.Accept(new WillVisitor());
}
结果:
长途汽车石家庄到邢台50元
生产力发达了人民幸福了,汽车票在原价的基础上,下调90%!
火车石家庄到邢台40元
生产力发达了人民幸福了,火车票在原价的基础上,下调90%!
Visitor模式通过所谓的双重分发(double dispatch)来实现在不更改Element类层次结构的前提下,在运行时透明的为类层次结构上的各个类动态添加新的操作。所谓双重分发即Visitor模式中间包括了两个多态分发:第一个为Accept方法的多态辨析;第二个为Visit方法的多态辨析(重载)
Visitor模式最大缺点在于扩展类层次结构(添加新的Element子类),会导致Visitor类的改变,因此Visitor模式使用户Element类层子结构稳定,而其中的操作却经常面临频繁改动。
当我们需要增加一个交通工具的子类时,我们需要给NotifyVisitor类添加一个Visit函数,并且NotifyVisitor的每个派生类也必须添加。