访问者模式
一、完成者信息:
姓名:王璐
学号:07770231
二、模式信息
模式名称:访问者模式
生活场景:假如以前食堂的食品和饮料一直都是一个价位,没有变动。这学期开学,由于工商局调节价位,导致普遍商品都涨价了,所以学校食堂里,菜价和饮料都涨价了。
终极目标:实现能够对所有的情况进行访问,通知发生变化。
不使用访问者模式
- 不假思索的思路:我们去吃饭,首先看菜单,来选择食物,用现在的菜单跟以前的菜单相对比,价位之间的变化。
类结构图:
代码实现:
(1)菜单
//菜单
public abstract class Menu
{
public abstract void OldPrice(); //以前价格
public abstract void NowPrice(); //涨价之后现在价格
}
(2)饮料
//饮料类
public class Drink:Menu
{
public override void OldPrice()
{
Console.WriteLine("康师傅绿茶以前食堂卖2.5元/瓶!");
}
public override void NowPrice()
{
Console.WriteLine("这学期开学,康师傅绿茶在原来基础上涨到3.0元/瓶!");
}
}
(3)食品
//食品类
public class Food:Menu
{
public override void OldPrice()
{
Console.WriteLine("食堂里原来每份菜5.0元/份!");
}
public override void NowPrice()
{
Console.WriteLine("这学期开学,每份菜涨到6.0元/份!");
}
}
(4)主函数
class Program
{
static void Main()
{
Drink drink = new Drink();
drink.OldPrice();
drink.NowPrice();
Console.WriteLine(" ");
Food food = new Food();
food.OldPrice();
food.NowPrice();
Console.Read();
}
}
运行结果:
存在问题:
在软件构建过程中,由于需求的改变,某些类层次结构中常常需要增加新的行为(方法),如果直接在基类中做这样的改变,将会给子类带来很繁重的变更负担,甚至破坏原有设计。
2. 归纳阶段:使用访问者模式
当前目标:增加一个抽象访问者与实现访问者。以实现价格的调整,上涨或者下降;加一个聚合类以实现菜单种类的变更,增加或减少。
类结构图:
代码实现:
(1) 菜单类
饮料和食品都得接受访问者菜单的通知
//菜单
public abstract class Menu
{
public abstract void OldPrice();
//这学期开学,食堂饭价涨价
public abstract void Accept(IndustryVisitor Visitor);
}
(2)饮料实现
//饮料
public class Drink:Menu
{
public override void OldPrice()
{
Console.WriteLine("康师傅绿茶以前食堂卖2.5元/瓶!");
}
public override void Accept(IndustryVisitor Visitor)
{
Visitor.Visit(this);
Console.WriteLine(" ");
}
}
(3) 食品实现
//食品
public class Food:Menu
{
public override void OldPrice()
{
Console.WriteLine("食堂里原来每份菜5.0元/份!"); ;
}
public override void Accept(IndustryVisitor Visitor)
{
Visitor.Visit(this);
}
}
(4)聚合类
用于确定价格变动的部门,可分配的:食品和饮料有多种,其中两种都在涨价
//工商管理部门
public class IndustryMangement
{
IList<Menu> _list = new List<Menu>();
public void Add(Menu menu)
{
_list.Add(menu);
}
public void Detach(Menu menu)
{
_list.Remove(menu);
}
public void Accept(IndustryVisitor visitor)
{
foreach (Menu m in _list)
{
m.Accept(visitor);
}
}
}
(5)抽象访问者
价格在变,所以不同时期,价格不同,价格为访问者。
//访问者抽象
public abstract class IndustryVisitor
{
public abstract void Visit(Drink drink);
public abstract void Visit(Food food);
internal void Visit()
{
throw new NotImplementedException();
}
}
(6)访问者实现
//新学期,饭价上涨
//目的通知涨价细节
public class New_SchoolTerm:IndustryVisitor
{
public override void Visit(Drink drink)
{
drink.OldPrice();
Console.WriteLine("这学期开学,康师傅绿茶在原来基础上涨到3.0元/瓶!");
}
public override void Visit(Food food)
{
food.OldPrice();
Console.WriteLine("这学期开学,每份菜涨到6.0元/份!");
}
}
(7)主函数
class Program
{
static void Main()
{
IndustryMangement mangement = new IndustryMangement();
mangement.Add(new Drink());
mangement.Add(new Food());
mangement.Accept(new New_SchoolTerm());
Console.Read();
}
}
3. 验证阶段
当前目标:在上面的基础上增加一个水果类。
类结构图:
代码实现:
(1)菜单类
饮料和食品都得接受访问者菜单的通知
//菜单
public abstract class Menu
{
public abstract void OldPrice();
//这学期开学,食堂饭价涨价
public abstract void Accept(IndustryVisitor Visitor);
}
(2)饮料实现
//饮料
public class Drink:Menu
{
public override void OldPrice()
{
Console.WriteLine("康师傅绿茶以前食堂卖2.5元/瓶!");
}
public override void Accept(IndustryVisitor Visitor)
{
Visitor.Visit(this);
Console.WriteLine(" ");
}
}
(3) 食品实现
//食品
public class Food:Menu
{
public override void OldPrice()
{
Console.WriteLine("食堂里原来每份菜5.0元/份!"); ;
}
public override void Accept(IndustryVisitor Visitor)
{
Visitor.Visit(this);
Console.WriteLine(" ");
}
}
(4) 水果实现
//水果实现
public class Fruit:Menu
{
public override void OldPrice()
{
Console.WriteLine("苹果在调价之前是2.5元/斤!");
}
public override void Accept(IndustryVisitor Visitor)
{
Visitor.Visit(this);
Console.WriteLine(" ");
}
}
(5)聚合类
用于确定价格变动的部门,可分配的:食品和饮料有多种,其中两种都在涨价
//工商管理部门
public class IndustryMangement
{
IList<Menu> _list = new List<Menu>();
public void Add(Menu menu)
{
_list.Add(menu);
}
public void Detach(Menu menu)
{
_list.Remove(menu);
}
public void Accept(IndustryVisitor visitor)
{
foreach (Menu m in _list)
{
m.Accept(visitor);
}
}
}
(6)抽象访问者
价格在变,所以不同时期,价格不同,价格为访问者。
//访问者抽象
public abstract class IndustryVisitor
{
public abstract void Visit(Drink drink);
public abstract void Visit(Food food);
public abstract void Visit(Fruit fruit);
internal void Visit()
{
throw new NotImplementedException();
}
}
(7)访问者实现
//新学期,饭价上涨
//目的通知涨价细节
public class New_SchoolTerm:IndustryVisitor
{
public override void Visit(Drink drink)
{
drink.OldPrice();
Console.WriteLine("这学期开学,康师傅绿茶在原来基础上涨到3.0元/瓶!");
}
public override void Visit(Food food)
{
food.OldPrice();
Console.WriteLine("这学期开学,每份菜涨到6.0元/份!");
}
public override void Visit(Fruit fruit)
{
fruit.OldPrice();
Console.WriteLine("调价之后苹果3.5元/斤!");
}
}
(8)主函数
class Program
{
static void Main()
{
IndustryMangement mangement = new IndustryMangement();
mangement.Add(new Drink());
mangement.Add(new Food());
mangement.Add(new Fruit());
mangement.Accept(new New_SchoolTerm());
Console.Read();
}
}
运行结果:
设计体会:
1.1.Visitor模式通过所谓双重分发(double dispatch)来实现在不更改Element类层次结构的前提下,在运行时透明地为类层次结构上的各个类动态添加新的操作。
2.所谓双重分发却Visotor模式中间包括了两个多态分发(注意其中的多态机制);第一个为accept方法的多态辨析;第二个为visitor方法的多态辨析。
4. 抽象阶段
概念:访问者模式的目的是封装一些施加于某种数据结构元素之上的操作。一旦这些操作需要修改的话,接受这个操作的数据结构则可以保持不变。
访问者模式适用于数据结构相对未定的系统,它把数据结构和作用于结构上的操作之间的耦合解脱开,使得操作集合可以相对自由地演化。数据结构的每一个节点都可以接受一个访问者的调用,此节点向访问者对象传入节点对象,而访问者对象则反过来执行节点对象的操作。这样的过程叫做"双重分派"。节点调用访问者,将它自己传入,访问者则将某算法针对此节点执行。
Visitor:抽象访问者:声明了一个或者多个访问操作,形成所有的具体元素角色必须实现的接口。
ConcreteVisitor:具体访问者:实现抽象访问者角色所声明的接口,也就是抽象访问者所声明的各个访问操作。
Element:抽象节点:声明一个接受操作,接受一个访问者对象作为一个参量。
ConcreteElement:抽象具体节点:实现了抽象元素所规定的接受操作。
ObjectStructure:对象结构类:有如下的一些责任,可以遍历结构中的所有元素;如果需要,提供一个高层次的接口让访问者对象可以访问每一个元素;如果需要,可以设计成一个复合对象或者一个聚集,如列(List)或集合(Set)。
访问者模式的适用性:
1.一个对象结构包含很多类对象,它们有不同的接口,而你想对这些对象实施一些依赖于其具体类的操作。
2.需要对一个对象结构中的对象进行很多不同的并且不相关的操作,而你想避免让这些操作"污染"这些对象的类。Visitor使得你可以将相关的操作集中起来定义在一个类中。当该对象结构被很多应用共享时,用Visitor模式让每个应用仅包含需要用到的操作。
3.定义对象结构的类很少改变,但经常需要在结构上定义新的操作。改变对象结构类需要重定义对所有访问者的接口,这可能需要很大的代价。如果对象结构类经常改变,那么可能还是在这些类中定义这些操作较好。