模式实例之——访问者实例

场景:长途汽车票、火车票价格调整

描述:春运开始了,人流高峰期,打工的人群要回家过年了,票价要涨了!

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的每个派生类也必须添加。

时间: 2024-10-06 11:27:38

模式实例之——访问者实例的相关文章

抽象工厂模式(Abstract Factory)C#实例

抽象工厂模式(Abstract Factory)C#实例 本文出处http://www.dofactory.com/net/abstract-factory-design-pattern 一.场景描述 本实例描述了抽象工厂模式的一个使用场景.在动物世界弱肉强食,食肉动物会吃掉食草动物.这是动物世界的规律,因此在动物世界类中有Runfoodchain(运行食物链)方法.在动物世界里总是有食肉动物和食草动物这两个抽象成员.它们之所以是抽象成员是因为他们不是具体的一种动物.而食草动物与食肉动物的区别在

android launchmode(四种启动模式)应用场景及实例

模式介绍 [1] standard 模式 这是默认模式,每次激活Activity时都会创建Activity实例,并放入任务栈中. [2] singleTop 模式 如果在任务的栈顶正好存在该Activity的实例,就重用该实例( 会调用实例的 onNewIntent() ),否则就会创建新的实例并放入栈顶,即使栈中已经存在该Activity的实例,只要不在栈顶,都会创建新的实例. [3] singleTask 模式 如果在栈中已经有该Activity的实例,就重用该实例(会调用实例的 onNew

MVC学习之数据库开发模式:代码优先实例介绍

数据库开发模式之代码优先主要有以下几步: 1.在Models文件夹中创建需要的表所对应的类 2.创建数据上下文类 3.在webConfig文件中配置数据库连接节点 4.添加控制器和相应的视图文件 5.在控制器的动作中创建数据上下文实例,通过实例操作数据库数据 具体步骤如下所示: 1.在Models文件夹中创建所需表对应的类 namespace CodeFirst.Models { public class book { public int Id { get; set; } public str

C#对象为Null模式(Null Object Pattern)实例教程

本文以实例形式简单讲述了C#对象为Null模式(Null Object Pattern),分享给大家供大家参考.具体实现方法如下: 所谓的"对象为Null模式",就是要求开发者考虑对象为Null的情况,并设计出在这种情况下的应对方法. 以前面" "一文中的例子来说,在我们的客户端程序中只考虑了用户输入1,2,3的情况,如果用户输入其它数字,比如4,就没有一个对应的IBall接口实现类实例产生,于是会报如下的错: 为了应对这种情况,我们专门设计一个类,当用户输入1,2

MVC学习之数据库开发模式:模型优先实例介绍

数据库开发模型之模型优先的步骤如下: 1.在数据库中建立一个空的数据库[不需要建立任何表]     如果不事先创建一个新的数据库,如果在连接数据库那步不选着数据库,默认是在Master数据库中生成表的2.添加ADO.NET实体数据模型     选中Models文件夹.添加--新建项--数据--ADO.NET实体数据模型--取名[文中取名为ModelFirst]--控模型--完成3.修改实体容器名称    双击ModelFirst.edmx,在左边的空白页面上单击右键--属性---修改实体容器名称

ORACLE11g R2【单实例 FS→单实例FS】

ORACLE11g R2[单实例 FS→单实例FS] 本演示案例所用环境:   primary standby OS Hostname pry std OS Version RHEL6.5 RHEL6.5 DB Version 11.2.0.4 11.2.0.4 db_name stephen stephen db_unique_name stephen standby service_names stephen standby instance_name stephen standby Pri

ORACLE10g R2【单实例 FS→单实例FS】

ORACLE10g R2[单实例FS→单实例FS] 本演示案例所用环境:   primary standby OS Hostname pry std OS Version RHEL5.8 RHEL5.8 DB Version 10.2.0.5 10.2.0.5 db_name stephen stephen db_unique_name stephen standby service_names stephen standby instance_name stephen standby Prim

单实例和多实例的区别以及用途

1.单实例和多实例 单实例一般会在类中自己实例化,通过getInstance获取该实例.ps: /** * 单例模式 */ public class Singleton { private static Singleton singleton = new Singleton(); private Singleton() {} public static Singleton getInstance() { return singleton; } 多实例则可以随意new.ps: public cla

Oracle Data Guard 单实例到单实例配置(物理standby)

接上一篇oracle dataguard概念篇,现在进行下详细的配置,此次配置的是单实例到单实例,也就是说主库和备库都是基于文件系统的单实例. 主机环境:Red Hat Enterprise Linux Server release 6.3 Rdbms版本:11.2.0.3 其他详细信息如下所示: 需要注意的是,两边的/etc/hosts文件保证都包含对方的ip对应关系 oracle安装的目录是相同的 编辑/etc/hosts,分别添加以下内容 192.168.1.241 db1 192.168