1823 访问者模式

    技术2022-07-11  99

    访问者模式(Visitor Pattern)

    将作用于某种数据结构中的各元素的操作分离出来封装成独立的类,使其在不改变数据结构的前提下可以添加作用于这些元素的新的操作,为数据结构中的每个元素提供多种访问方式。它将对数据的操作与数据结构进行分离,是行为类模式中最复杂的一种模式。

    ps:很少用,但是如果用了就说明一定要用它(除了乱用)

    1.案例说明

    需求描述

    小组有恒定的成员xiaocai、xiaozhang、xiaoxu[暂定为三个不同的对象]现在每个人在上班期间都要进行代码工作的编写

    1.1 初始代码编写

    public abstract class IStudent { public abstract string StudentName { get; } public virtual void DoSomething() { Console.WriteLine($"{StudentName}:写代码、改代码..."); } } public class XiaocaiStudent : IStudent { public override string StudentName { get { return "Xiaocai"; } } } public class XiaoXuStudent : IStudent { public override string StudentName { get { return "XiaoXu"; } } } public class XiaoZhangStudent : IStudent { public override string StudentName { get { return "XiaoZhangStudent"; } } } ///界面调用 private void btnSimple_Click(object senderEventArgs e) { IStudent pXiaocai = new XiaocaiStudent(); pXiaocai.DoSomething(); IStudent pXiaoZhang = new XiaoZhangStudent(); pXiaoZhang.DoSomething(); IStudent pXiaoXu = new XiaoXuStudent(); pXiaoXu.DoSomething(); }

    效果如下:

    1.2 需求发生更新

    现在在写代码的事情是在上班的时候干的事情,现在中午了,现在我们现在想知道他们在干什么,那么xiaocai中午会看技术视频[手动狗头emmm脸上贴点金];xiaozhang会热饭,然后和自家小孩视频;xiaoxu会出去找吃的,然后会午休。

    那么这样的话我们需要兼容之前的上班状态;增加枚举来控制状态我们现在需要让他们自己要做一些事情。让DoSomething抽象,子类必须实现

    代码如下:

    public enum StateType { /// <summary> /// 工作时间 /// </summary> Working=0, /// <summary> /// 中午休息时间 /// </summary> MidDayRest=1, } public abstract class IStudent { public abstract string StudentName { get; } public abstract void DoSomething(StateType pStateType); } public class XiaocaiStudent : IStudent { public override string StudentName { get { return "Xiaocai"; } } public override void DoSomething(StateType pStateType) { switch (pStateType) { case StateType.Working: Console.WriteLine($"工作时间:{StudentName}-写代码、改代码..."); break; case StateType.MidDayRest: Console.WriteLine($"中午休息时间:{StudentName}-看视频..."); break; } } } public class XiaoXuStudent : IStudent { public override string StudentName { get { return "XiaoXu"; } } public override void DoSomething(StateType pStateType) { switch (pStateType) { case StateType.Working: Console.WriteLine($"工作时间:{StudentName}-写代码、改代码..."); break; case StateType.MidDayRest: Console.WriteLine($"中午休息时间:{StudentName}-会热饭,然后和自家小孩视频..."); break; } } } public class XiaoZhangStudent : IStudent { public override string StudentName { get { return "XiaoZhang"; } } public override void DoSomething(StateType pStateType) { switch (pStateType) { case StateType.Working: Console.WriteLine($"工作时间:{StudentName}-写代码、改代码..."); break; case StateType.MidDayRest: Console.WriteLine($"中午休息时间:{StudentName}-出去找吃的,然后会午休..."); break; } } } ///界面调用 private void btnExtend01_Click(object sender, EventArgs e) { Console.WriteLine("*********************上班时间*********************************"); { IStudent pXiaocai = new XiaocaiStudent(); pXiaocai.DoSomething(StateType.Working); IStudent pXiaoZhang = new XiaoZhangStudent(); pXiaoZhang.DoSomething(StateType.Working); IStudent pXiaoXu = new XiaoXuStudent(); pXiaoXu.DoSomething(StateType.Working); } Console.WriteLine("*********************午休时间*********************************"); { IStudent pXiaocai = new XiaocaiStudent(); pXiaocai.DoSomething(StateType.MidDayRest); IStudent pXiaoZhang = new XiaoZhangStudent(); pXiaoZhang.DoSomething(StateType.MidDayRest); IStudent pXiaoXu = new XiaoXuStudent(); pXiaoXu.DoSomething(StateType.MidDayRest); } }

    效果如下:

    1.3当然也可以在类中扩展如下:

    public abstract class IStudent { public abstract string StudentName { get; } public void DoSomething() { Console.WriteLine($"工作时间:{StudentName}-写代码、改代码..."); } public abstract void DoSomething(StateType pStateType); }

    1.4 爆炸增加

    如果需求到1.2、1.3不再发生增加,那么我们也就不再需要扩展,但是…

    扩展,而且扩展的频率和幅度逐渐变大,如:晚上加班情况、晚上回家之后的状态、周末状态、甚至于周一上午状态、下午状态… 想想我们的扩展…动态变化:现在我们想让状态可以动态切换… 基于上述的变化,其实我们就来了今天设计模式的主角,他就是访问者,我们将每个人在干什么作为一个访问者,上班状态就是上班访问者,午休状态就是午休访问者,等等…对于动态变化状态那么就是动态切换访问者…

    代码实现:

    对象 public abstract class IStudent { public abstract string StudentName { get; } public abstract void DoSomething(IVisitor pVisitor); } public class XiaocaiStudent : IStudent { public override string StudentName { get { return "Xiaocai"; } } public override void DoSomething(IVisitor pVisitor) { pVisitor.XiaocaiDoSomething(this); } } public class XiaoXuStudent : IStudent { public override string StudentName { get { return "XiaoXu"; } } public override void DoSomething(IVisitor pVisitor) { pVisitor.XiaoXuDoSomething(this); } } public class XiaoZhangStudent : IStudent { public override string StudentName { get { return "XiaoZhang"; } } public override void DoSomething(IVisitor pVisitor) { pVisitor.XiaoZhangDoSomething(this); } } ///访问者 public interface IVisitor { void XiaocaiDoSomething(XiaocaiStudent pXiaocaiStudent); void XiaoZhangDoSomething(XiaoZhangStudent pXiaoZhangStudent); void XiaoXuDoSomething(XiaoXuStudent pXiaoXuStudent); } public class VisitorWorking : IVisitor { public void XiaocaiDoSomething(XiaocaiStudent pXiaocaiStudent) { Console.WriteLine($"工作时间:{pXiaocaiStudent.StudentName}-写代码、改代码..."); } public void XiaoZhangDoSomething(XiaoZhangStudent pXiaoZhangStudent) { Console.WriteLine($"工作时间:{pXiaoZhangStudent.StudentName}-写代码、改代码..."); } public void XiaoXuDoSomething(XiaoXuStudent pXiaoXuStudent) { Console.WriteLine($"工作时间:{pXiaoXuStudent.StudentName}-写代码、改代码..."); } } public class VisitorMidDayRest : IVisitor { public void XiaocaiDoSomething(XiaocaiStudent pXiaocaiStudent) { Console.WriteLine($"中午休息时间:{pXiaocaiStudent.StudentName}-看视频..."); } public void XiaoZhangDoSomething(XiaoZhangStudent pXiaoZhangStudent) { Console.WriteLine($"中午休息时间:{pXiaoZhangStudent.StudentName}-会热饭,然后和自家小孩视频..."); } public void XiaoXuDoSomething(XiaoXuStudent pXiaoXuStudent) { Console.WriteLine($"中午休息时间:{pXiaoXuStudent.StudentName}-出去找吃的,然后会午休..."); } } ///界面调用 private void btnVisitor_Click(object sender, EventArgs e) { Console.WriteLine("*********************上班时间*********************************"); { IVisitor pVisitor = new VisitorWorking(); IStudent pXiaocai = new XiaocaiStudent(); pXiaocai.DoSomething(pVisitor); IStudent pXiaoZhang = new XiaoZhangStudent(); pXiaoZhang.DoSomething(pVisitor); IStudent pXiaoXu = new XiaoXuStudent(); pXiaoXu.DoSomething(pVisitor); } Console.WriteLine("*********************午休时间*********************************"); { IVisitor pVisitor = new VisitorMidDayRest(); IStudent pXiaocai = new XiaocaiStudent(); pXiaocai.DoSomething(StateType.MidDayRest); IStudent pXiaoZhang = new XiaoZhangStudent(); pXiaoZhang.DoSomething(StateType.MidDayRest); IStudent pXiaoXu = new XiaoXuStudent(); pXiaoXu.DoSomething(StateType.MidDayRest); } }

    效果如下:

    在Visitor中使用具体对象就是为了将每个函数的对象具化,强类型化。之前1.2、1.3都是采用横向扩展,没有办法[其实也有办法,比如[扩展类、分布类]+[反射/dynamic]的方式]做到动态扩展[采用代码反射等技术,不修改代码,对修改关闭、对扩展开放],现在纵向扩展访问者那么也就可以做到了。

    2.访问者模式入土[局限]:

    访问者模式由于是横向扩展的,在Visitor中使用具体对象就是为了将每个函数的对象具化,强类型化。所以会导致…

    现在在成员中增加成员XiaoZhao,所有的访问者类体系基本上是毁灭性的…

    没错在这个局限下他入土了。

    3.总结

    访问者模式用的好一剑封喉,用的不好[局限]一剑封喉…设计模式都有长处也有弊端,适当的使用,是体现内功也是体现经验的地方…

    以上总结也是我认为设计模式更像招式,比如独孤九剑,有总决式、破剑式、破刀式…

    Processed: 0.011, SQL: 9