24、C# 设计模式-访问者模式

访问者模式(Vistor Pattern)

访问者模式属于行为型模式,表示一个作用于某对象结构中的各元素的操作。它使你可以在不改变各元素类的前提下定义作用于这些元素的新操作。

结构对象是使用访问者模式必备条件,而且这个结构对象必须存在遍历自身各个对象的方法。

角色:

1、 抽象访问者(Visitor);

为该对象结构中具体元素角色声明一个访问操作接口。该操作接口的名字和参数标识了发送访问请求给具体访问者的具体元素角色,这样访问者就可以通过该元素角色的特定接口直接访问它;

2、 具体访问者(ConcreteVisitor);

实现Visitor声明的接口;

3、 抽象元素(Element);

定义一个接受访问操作,它以一个访问者(Visitor)作为参数;

4、 具体元素(ConcreteElement);

实现了抽象元素所定义的接受操作接口;

5、 结构对象(ObjectStructure);

可以提供一个高层接口以允许访问者访问它的元素。

示例:

 

命名空间VistorPattern中包含Student学生基类充当抽象元素,它的3个实现类Kris、Cherry和Harling分别是我的儿子、女儿和侄女的英文名,它们充当具体元素角色。ITeacher接口充当抽象访问者,而Teacher类充当具体访问者。花名册类Roster充当结构对象。本案例尝试使用老师根据花名册来进行家访这样的场景来讲述访问者模式的使用方法。

namespace VistorPattern
public abstract class Student {

    public string Name { get; protected set; }

    public Student(string name) {
        Name = name;
    }

    public abstract void Accept(ITeacher teacher);

}

学生基类Student,代表抽象元素。

public class Kris : Student {

    public Kris(string name) : base(name) {

    }

    public override void Accept(ITeacher teacher) {
        teacher.Visit(this);
    }

}

Kris类,代表某一具体学生。

public class Cherry : Student {

    public Cherry(string name) : base(name) {

    }

    public override void Accept(ITeacher teacher) {
        teacher.Visit(this);
    }

}

Cherry类,代表某一具体学生。

public class Harling : Student {

    public Harling(string name) : base(name) {

    }

    public override void Accept(ITeacher teacher) {
        teacher.Visit(this);
    }

}

Harling类,代表某一具体学生。

public interface ITeacher {

    void Visit(Student student);

}

ITeacher接口,代表抽象访问者。

public class Teacher : ITeacher {

    private string _name = null;

    public Teacher(string name) {
        _name = name;
    }

    public void Visit(Student student) {
        Console.WriteLine($"Mr.{_name} is going to visit {student.Name}'s home. ");
    }

}

Teacher类,代表具体访问者。

public class Roster {

    public List<Student> Students { get; private set; }

    public Roster() {
        Students = new List<Student> {
            new Cherry("Cherry"),
            new Kris("Kris"),
            new Harling("Harling")
        };
    }

}

Roster类,代表花名册,充当结构对象,老师根据这个对象来进行家访。

public class Program {

    public static void Main(string[] args) {
        Roster roster = new Roster();
        foreach(var student in roster.Students) {
            student.Accept(new Teacher("Tony"));
        }

        Console.Read();
    }

}

以上是调用方的代码,以下是这个案例的输出结果:

Mr.Tony is going to visit Cherry's home.
Mr.Tony is going to visit Kris's home.
Mr.Tony is going to visit Harling's home.

优点:

1、 符合单一职责原则,凡是适用访问者模式的场景中,元素类中需要封装在访问者中的操作必定是与元素类本身关系不大且是易变的操作,使用访问者模式一方面符合单一职责原则,另一方面,因为被封装的操作通常来说都是易变的,所以当发生变化时,就可以在不改变元素类本身的前提下,实现对变化部分的扩展;
2、 扩展性良好,元素类可以通过接受不同的访问者来实现对不同操作的扩展;

缺点:

1、 增加新的元素类变得困难,每增加一个新的元素意味着要在抽象访问者角色中增加一个新的抽象操作,并在每一个具体访问者类中添加相应的具体操作;

使用场景:

1、 一个对象结构包含很多类对象,它们有不同的接口,而你想对这些对象实施一些依赖于其具体类的操作;
2、 需要对一个对象结构中的对象进行很多不同的并且不相关的操作,而你想避免让这些操作“污染”这些对象的类Visitor模式使得你可以将相关的操作集中起来定义在一个类中;
3、 当该对象结构被很多应用共享时,用Visitor模式让每个应用仅包含需要用到的操作;
4、 定义对象结构的类很少改变,但经常需要在此结构上定义新的操作改变对象结构类需要重定义对所有访问者的接口,这可能需要很大的代价如果对象结构类经常改变,那么可能还是在这些类中定义这些操作较好;