设计模式-行为型-访问者模式

访问者模式



前言

在生活中,针对同一件物品,因为观察角度不同,所得到的结果也有所不同,访问者以某一类的物品触发,详细实现了多角度多物品的观察结果,比如规则性物体,可以抽象出来连个元素,正方体和长方体。抽象访问者可以抽象出来观察的角度,例如正视图,左视图,俯视图,然后由具体访问者实现观察角度,子类与实际元素类数量相同,可以使不同的物体具有相同的观察操作,通过结构对象维护元素集合,提供方法接受访问者对元素进行操作。


一、访问者模式(Visitor)

可以将数据结构与数据操作分离,比如封装一些操作某种数据结构的算法,可以在不改变数据结构的前提下,定义元素的新操作。

二、角色

  1. 抽象访问者(IVisitor):接口或抽象类,定义了一个visit方法用于访问每个具体的元素,参数时具体对象。
  2. 具体访问者(Concret Visitor):实现对具体结构对象的元素的操作。
  3. 抽象元素(IElement):接口或抽象类,定义了一个接受访问者访问的方法accept(),表示所有元素类型都支持被访问者访问。
  4. 具体元素(Concrete Element): 接受访问者的具体实现。
  5. 结构对象(Object Structure):维护元素集合,并提供方法接受访问者对该集合所有元素进行操作。

三、应用场景

  • 数据结构稳定,数据结构操作经常变化的场景
  • 数据结构与数据操作分离的场景
  • 需要对不同数据类型(元素)进行操作,而不使用分支判断具体类型的场景。

四、代码实现

1. 抽象元素

代码如下(示例): 抽象被访问者,定义某类元素的抽象

 	/// <summary>
    /// 抽象元素
    /// 抽象被访问者
    ///
    /// </summary>
    public interface IEmployee
    {
        public string Name { get; set; }
        public string Depament { get; set; }
        public int KPI { get; set; }

        void Accept(IKPIVisitor kPIVisitor);
    }

2.具体元素

代码如下(示例):被访问的元素以及被访问的方法

	/// <summary>
    /// 被访问的元素以及被访问的方法
    /// </summary>
    public class Dev : IEmployee
    {
        public Dev(string Name)
        {
            this.Name = Name;
            this.Depament = "开发";
            this.KPI = new Random().Next(10);
        }
        public int KPI { get; set; }
        public string Name { get; set; }
        public string Depament { get; set; }

        public void Accept(IKPIVisitor kPIVisitor)
        {
            kPIVisitor.KPIVisitor(this);
        }
        public int CodeRowCount()
        {
            var bugNumber = new Random().Next(100000);
            Console.WriteLine($"部门{this.Depament},{this.Name}写出{bugNumber}行代码");
            return bugNumber;
        }
    }
    /// <summary>
    /// 被访问的元素以及被访问的方法
    /// </summary>
    public class ProjectManager : IEmployee
    {
        public ProjectManager(string Name)
        {
            this.Name = Name;
            this.Depament = "PM";
            this.KPI = new Random().Next(10);
        }
        public int KPI { get; set; }
        public string Name { get; set; }
        public string Depament { get; set; }

        public void Accept(IKPIVisitor kPIVisitor)
        {
            kPIVisitor.KPIVisitor(this);
        }
        public int DomandaEffettiva()
        {
            var bugNumber = new Random().Next(10000);
            Console.WriteLine($"部门{this.Depament},{this.Name}提出{bugNumber}个有效需求");
            return bugNumber;
        }
    }
    /// <summary>
    /// 被访问的元素以及被访问的方法
    /// </summary>
    public class Tester : IEmployee
    {
        public Tester(string Name)
        {
            this.Name = Name;
            this.Depament = "测试";
            this.KPI = new Random().Next(10);
        }
        public int KPI { get; set; }
        public string Name { get; set; }
        public string Depament { get; set; }

        public void Accept(IKPIVisitor kPIVisitor)
        {
            kPIVisitor.KPIVisitor(this);
        }
        public int BugNumber()
        {
            var bugNumber = new Random().Next(10000);
            Console.WriteLine($"部门{this.Depament},{this.Name}测试出BUG{bugNumber}个");
            return bugNumber;
        }
    }

3.定义提供给访问者的抽象方法

代码如下(示例):所有具体访问者都继承自这个抽象,然后在继承者内进行内部实现,实现子类内部关注内容,比如,技术总监关注具体的工作内容,CEO关注任务的完成度

 	/// <summary>
    /// 定义提供给访问者的抽象方法
    /// 所有具体访问者都继承自这个抽象,然后在继承者内进行内部实现,实现子类内部关注内容
    /// 比如,技术总监关注具体的工作内容,CEO关注任务的完成度
    /// </summary>
    public interface IKPIVisitor
    {
        /// <summary>
        /// 开发的KPI实现
        /// </summary>
        /// <param name="employee"></param>
        void KPIVisitor(Dev employee);
        /// <summary>
        /// 产品的KPI实现
        /// </summary>
        /// <param name="employee"></param>
        void KPIVisitor(ProjectManager employee);
        /// <summary>
        /// 测试的KPI实现
        /// </summary>
        /// <param name="employee"></param>
        void KPIVisitor(Tester employee);
    }

4.CEO关注KPI

代码如下(示例):

	/// <summary>
    /// CEO关注KPI
    /// </summary>
    public class CEOVisitor : IKPIVisitor
    {
        public void KPIVisitor(Dev employee)
        {
            Console.WriteLine($"{employee.Depament}:{employee.Name},KPI为{employee.KPI}");
        }

        public void KPIVisitor(ProjectManager employee)
        {
            Console.WriteLine($"{employee.Depament}:{employee.Name},KPI为{employee.KPI}");
        }

        public void KPIVisitor(Tester employee)
        {
            Console.WriteLine($"{employee.Depament}:{employee.Name},KPI为{employee.KPI}");
        }
    }
    /// <summary>
    /// CTo关注的所有内容实现
    /// </summary>
    public class CTOVisitor : IKPIVisitor
    {
        /// <summary>
        /// CTO关注开发的代码行数
        /// </summary>
        /// <param name="employee"></param>
        public void KPIVisitor(Dev employee)
        {
            Console.WriteLine($"{employee.Depament}:{employee.Name},代码行数为{employee.CodeRowCount()}");
        }
        /// <summary>
        /// CTO关注产品的有效需求
        /// </summary>
        /// <param name="employee"></param>
        public void KPIVisitor(ProjectManager employee)
        {
            Console.WriteLine($"{employee.Depament}:{employee.Name},有效需求{employee.DomandaEffettiva()}");
        }
        /// <summary>
        /// CTO关注测试的BUG数
        /// </summary>
        /// <param name="employee"></param>
        /// <exception cref="NotImplementedException"></exception>
        public void KPIVisitor(Tester employee)
        {
            Console.WriteLine($"{employee.Depament}:{employee.Name},BUG数{employee.BugNumber()}");
        }
    }

5.结构化对象

代码如下(示例):结构化对象

	/// <summary>
    /// 结构化对象
    /// </summary>
    public class BussingssReposrt
    {
        private List<IEmployee> employees = new List<IEmployee>();
        public BussingssReposrt()
        {
            employees.Add(new Dev("开发菜鸟"));
            employees.Add(new Dev("开发菜鸡"));
            employees.Add(new ProjectManager("产品菜鸟"));
            employees.Add(new ProjectManager("产品菜鸡"));
            employees.Add(new Tester("测试大牛"));
        }
        public void showReport(IKPIVisitor kPIVisitor)
        {
            foreach (var item in employees)
            {
                item.Accept(kPIVisitor);
            }
        }
    }

6.访问客户端

代码如下(示例):结构化对象

	 public class EmployeeClient {
        public void clientMain() {
            BussingssReposrt bussingssReposrt = new BussingssReposrt();
            Console.WriteLine("----------------------------CEO------------------------------");
            bussingssReposrt.showReport(new CEOVisitor());
            Console.WriteLine("----------------------------CTO------------------------------");
            bussingssReposrt.showReport(new CTOVisitor());
        }
    }

7.代码输出

代码如下(示例):


----------------------------CEO------------------------------
开发:开发菜鸟,KPI为3
开发:开发菜鸡,KPI为7
PM:产品菜鸟,KPI为5
PM:产品菜鸡,KPI为0
测试:测试大牛,KPI为4
----------------------------CTO------------------------------
部门开发,开发菜鸟写出17530行代码
开发:开发菜鸟,代码行数为17530
部门开发,开发菜鸡写出75555行代码
开发:开发菜鸡,代码行数为75555
部门PM,产品菜鸟提出2673个有效需求
PM:产品菜鸟,有效需求2673
部门PM,产品菜鸡提出1719个有效需求
PM:产品菜鸡,有效需求1719
部门测试,测试大牛测试出BUG9341个
测试:测试大牛,BUG数9341

总结

相同的数据结构,观察角度不同,生成的报表内容不同。

优点:

  • 数据结构与数据操作解耦,使操作集合可以独立变化
  • 扩展访问者角色,实现对数据集群的不同操作,程序扩展性更高
  • 元素具体类型并非单一,访问者均可操作
  • 各角色职责分离,符合单一职责原则。

缺点:

  • 无法增加元素类型
  • 具体元素变更困难
  • 违背了依赖倒置原则,
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

kenny@chen

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值