访问者模式
本篇博客将介绍访问者模式,访问者模式是一种较为复杂的行为型设计模式,它包含访问者和被访问者两个重要组成部分,这些被访问元素具有不同的类型,且不同的访问者可以对其施加不同的访问操作。当我们的数据集合存在多种不同的访问方式的时候,我们可以使用访问者模式。
模式分类
行为型设计模式。
模式产生的原因
在医生开具处方单后,很多医院都存在这样的处理流程:划价人员拿到处方单之后根据药品名称和数量计算总价,药房工作人员根据药品名称和数量准备药品。我们这里可以将处方单看作是一个数据集合,其中包含了多种不同类型的药物信息,不同类型的工作人员在操作同一个药品信息集合时会提供不同的处理方法,而且可能会增加新类型的工作人员来操作处方单。
在软件开发中,有时也需要处理像是处方单这样的集合对象结构,在该对象中存储了多种不同类型的对象信息,而且对于同一个对象的操作可能会不一样。可能需要提供多种数据处理方法,还可能会增加新的数据处理方法,为了解决这种问题,访问者模式诞生了,他可以以不同的方式操作复杂对象结构。
模式类图
由上图可知,访问者模式包含以下5个对象:
Visitor(抽象访问者):
抽象访问者为每一个具体的元素类声明了一个访问操作,具体的操作将由其子类实现。
ConcreteVisitor(具体访问者):
具体访问者实现了抽象访问者为每一个具体元素类声明的抽象操作。
Element(抽象元素):
抽象元素声明一个Accept方法,用于接收访问者的访问,参数列表中需要传入一个访问者。
ConcreteElement(具体元素):
具体元素实现抽象元素中的Accept方法。
ObjectStructure(对象结构):
对象结构是一个元素集合用于存放元素对象,此项并不是必须要有的一个对象。
代码实现
例子:某软件公司要为某高校开发一套奖励审批系统,该系统可以实现教师奖励和学生奖励的审批,如果教师发表的论文数超过10篇或者学生论文数超过2篇就可以评选科研奖,如果教师教学分大于等于90分或者学生的平均成绩大于等于90分就可以评选成绩优秀奖。试使用访问者模式模拟该系统。
抽象访问者:审批系统:
using Visitor.Visitor.Question5.MyPerson;
namespace Visitor.Visitor.Question5
{
public abstract class AwardCheckSystem
{
public abstract void Check(Teacher teacher);
public abstract void Check(Student student);
}
}
具体访问者:科研审批系统:
using System;
using Visitor.Visitor.Question5.MyPerson;
namespace Visitor.Visitor.Question5.MyAwardCheck
{
public class ExcellenceAwardCheckSystem : AwardCheckSystem
{
public override void Check(Teacher teacher)
{
if (teacher.Info.EssayNum > 10)
{
Console.WriteLine($"{teacher.Info.Name}老师获得科研奖");
}
}
public override void Check(Student student)
{
if (student.Info.EssayNum > 10)
{
Console.WriteLine($"{student.Info.Name}学生获得科研奖");
}
}
}
}
具体访问者:成绩优秀审批系统:
using System;
using Visitor.Visitor.Question5.MyPerson;
namespace Visitor.Visitor.Question5.MyAwardCheck
{
public class ScientificAwardCheckSystem : AwardCheckSystem
{
public override void Check(Teacher teacher)
{
if (teacher.Info.Score >= 90)
{
Console.WriteLine($"{teacher.Info.Name}老师获得成绩优秀奖");
}
}
public override void Check(Student student)
{
if (student.Info.Score >= 90)
{
Console.WriteLine($"{student.Info.Name}学生获得成绩优秀奖");
}
}
}
}
基础数据类:
namespace Visitor.Visitor.Question5.MyPerson.Info
{
public class BaseInfo
{
public string Name;
public int Score;
public int EssayNum;
}
}
抽象访问元素:人类:
using Visitor.Visitor.Question5.MyPerson.Info;
namespace Visitor.Visitor.Question5
{
public abstract class Person
{
private BaseInfo _info;
public BaseInfo Info
{
get => _info;
set => _info = value;
}
public Person(BaseInfo info)
{
Info = info;
}
public abstract void Check(AwardCheckSystem awardCheckSystem);
}
}
具体元素:学生:
using Visitor.Visitor.Question5.MyPerson.Info;
namespace Visitor.Visitor.Question5.MyPerson
{
public class Student : Person
{
public Student(BaseInfo info) : base(info)
{
}
public override void Check(AwardCheckSystem awardCheckSystem)
{
awardCheckSystem.Check(this);
}
}
}
具体元素:教师:
using Visitor.Visitor.Question5.MyPerson.Info;
namespace Visitor.Visitor.Question5.MyPerson
{
public class Teacher : Person
{
public Teacher(BaseInfo info) : base(info)
{
}
public override void Check(AwardCheckSystem awardCheckSystem)
{
awardCheckSystem.Check(this);
}
}
}
对象结构:人类集合:
using System.Collections.Generic;
namespace Visitor.Visitor.Question5
{
public class PersonList
{
private static PersonList _instance;
public static PersonList GetInstance
{
get
{
if (_instance == null)
{
_instance = new PersonList();
}
return _instance;
}
}
private List<Person> _persons = new List<Person>();
public void Add(Person person)
{
_persons.Add(person);
}
public void Add(params Person[] person)
{
for (int i = 0; i < person.Length; i++)
{
_persons.Add(person[i]);
}
}
public void Remove(Person person)
{
_persons.Remove(person);
}
public void Check(AwardCheckSystem awardCheckSystem)
{
for (int i = 0; i < _persons.Count; i++)
{
_persons[i].Check(awardCheckSystem);
}
}
}
}
Program类:
using Visitor.Visitor.Example;
using Visitor.Visitor.Example.MyDepartment;
using Visitor.Visitor.Example.MyEmployee;
using Visitor.Visitor.Question5;
using Visitor.Visitor.Question5.MyAwardCheck;
using Visitor.Visitor.Question5.MyPerson;
using Visitor.Visitor.Question5.MyPerson.Info;
namespace Visitor
{
internal class Program
{
public static void Main()
{
Teacher teacher1 = new Teacher(new BaseInfo()
{
Name = "张无忌",
EssayNum = 20,
Score = 100
});
Teacher teacher2 = new Teacher(new BaseInfo()
{
Name = "张铁蛋",
EssayNum = 5,
Score = 100
});
Teacher teacher3 = new Teacher(new BaseInfo()
{
Name = "王五",
EssayNum = 1,
Score = 10
});
Student student1 = new Student(new BaseInfo()
{
Name = "赵六",
EssayNum = 4,
Score = 50
});
Student student2 = new Student(new BaseInfo()
{
Name = "张三",
EssayNum = 10,
Score = 70
});
Student student3 = new Student(new BaseInfo()
{
Name = "钱七",
EssayNum = 50,
Score = 100
});
PersonList.GetInstance.Add(student1, student2, student3, teacher1, teacher2, teacher3);
PersonList.GetInstance.Check(new ExcellenceAwardCheckSystem());
PersonList.GetInstance.Check(new ScientificAwardCheckSystem());
}
}
}
访问者模式总结
访问者模式的优点:
- 访问者模式增加新的访问操作很方便。
- 访问者模式让用户能够在不修改现有元素层次结构的情况下,定义作用于该层次结构的操作。
访问者模式的缺点:
- 增加新的元素类很困难。这点其实和抽象工厂是一个问题,属于开闭原则倾斜性。