本文作者:小黑黑

[设计模式]行为型模式之访问者模式

小黑黑 1年前 ( 2019-03-06 ) 468 抢沙发
[设计模式]行为型模式之访问者模式摘要: 一、引言        访问者模式,是【行为型】设计模式中的一种。访问者模式是一种将数据操作与数据结...

一、引言

        访问者模式,是【行为型】设计模式中的一种。访问者模式是一种将数据操作与数据结构分离的设计模式,它可以算是23中设计模式中最复杂的一个,但它的使用频率并不是很高,大多数情况下,你并不需要使用访问者模式。访问者模式理解起来还是有些难度的,用一个例子来说说访问者模式吧,我们都知道财务都是有记账本的,这个账本可以看做是一个对象,里面有自己的属性和字段,但只有两种方法,一个是收入,一个是支出,而查看账本的人可能有很多,比如我们的老板,财务主管,会计等等。而这些人在查看账本的时候的行为或者说是目的是不同的,这个时候我们就可以使用访问者模式,将账本的字段和属性与操作分离出来。

二、动机

        在软件构建过程中,由于需求的改变,某些类层次结构中常常需要增加新的行为(方法),如果直接在基类中做这样的更改,将会给子类带来很繁重的变更负担,甚至破坏原有设计。如何在不更改类层次结构的前提下,在运行时根据需求透明地为类层次结构上的各个类动态添加新的事件,从而避免上述问题?

三、意图

        表示一个作用于某对象结构中的各个元素的操作。它可以在不改变个元素的类的前提下定义作用于这些元素的新的操作。            --《设计模式》 GoF

四、结构图

123.PNG

4.1  模式的组成

        1、抽象访问者角色(Visitor):声明一个包括多个访问操作,有多少具体节点访问者就有几个访问操作。

        2、具体访问者角色(ConcreteVisitor):实现抽象访问者提供的接口。

        3、抽象节点角色(Element):声明一个接受操作,接受一个访问者对象作为参数。

        4、具体节点角色(ConcreteElement):实现抽象节点角色所规定的接口。

        5、结构对象角色(ObjectStructure):节点的容器,可以包含多个不同类或接口的容器。

五、访问者模式的具体实现

        下面我们将拿开头说到的例子来看看访问者模式的具体实现。

    /// <summary>
    /// 账单抽象类,相当于Element
    /// </summary>
    public abstract class Bill
    {

        public abstract void Accept(AccountBookVisitor vistor);
    }
    
    /// <summary>
    /// 支出的账单,具体节点角色
    /// </summary>
    public class ConsumptionBill : Bill
    {

        public decimal Money;

        public string Item;

        public ConsumptionBill(decimal money, string item)
        {
            this.Money = money;
            this.Item = item;
        }


        public override void Accept(AccountBookVisitor vistor)
        {
            vistor.View(this);
        }
    }
    
    /// <summary>
    /// 收入账单,具体节点角色
    /// </summary>
    public class IncomeBill : Bill
    {
        public decimal Money;

        public string Item;

        public IncomeBill(decimal money, string item)
        {
            this.Money = money;
            this.Item = item;
        }

        public override void Accept(AccountBookVisitor vistor)
        {
            vistor.View(this);
        }
    }
    
    /// <summary>
    /// 抽象访问者类:AccountBookVisitor
    /// </summary>
    public abstract class AccountBookVisitor
    {

        /// <summary>
        /// 查看支出的账单
        /// </summary>
        public abstract void View(ConsumptionBill bill);

        /// <summary>
        /// 查看收入的账单
        /// </summary>
        public abstract void View(IncomeBill bill);
    }
    
    /// <summary>
    /// 具体访问者类:老板
    /// </summary>
    public class BoosVisitor : AccountBookVisitor
    {
        /// <summary>
        /// 查看消费的方法,只关心支出金额和条目
        /// </summary>
        public override void View(ConsumptionBill bill)
        {
            Console.WriteLine($"老板查看一共支出了多少,金额:{bill.Money},具体条目:{bill.Item}");
        }

        /// <summary>
        /// 查看收入的方法,只关心收入金额和条目
        /// </summary>
        /// <param name="bill"></param>
        public override void View(IncomeBill bill)
        {
            Console.WriteLine($"老板查看一共收入了多少,金额:{bill.Money},具体条目:{bill.Item}");
        }
    }
    
    /// <summary>
    /// 具体访问者类:会计
    /// </summary>
    public class AccountingVisitor : AccountBookVisitor
    {
        /// <summary>
        /// 会计不光要看支出明细,如果是工资的话,还要查看是否缴纳个人所得税
        /// </summary>
        public override void View(ConsumptionBill bill)
        {
            Console.WriteLine($"查看支出明细,支出金额:{bill.Money}, 条目:{bill.Item}");
            if (bill.Item.Equals("工资"))
            {
                Console.WriteLine("查看是否已经缴纳个人所得税");
            }
        }

        /// <summary>
        /// 会计不光要看收入明细,还要看是否交税
        /// </summary>
        public override void View(IncomeBill bill)
        {
            Console.WriteLine($"查看收入明细,收入金额:{bill.Money}, 条目:{bill.Item}");
            Console.WriteLine("查看是否交税");
        }
    }
    
    /// <summary>
    /// 账本类,结构对象角色
    /// </summary>
    public class AccountBook
    {
        //账单集合
        private List<Bill> billList = new List<Bill>();
        
        /// <summary>
        /// 添加账单
        /// </summary>
        public void AddBill(Bill bill)
        {
            if (!billList.Contains(bill))
            {
                billList.Add(bill);
            }
        }

        /// <summary>
        /// 供账本的查看着查看账单
        /// </summary>
        /// <param name="visitor"></param>
        public void Show(AccountBookVisitor visitor)
        {
            foreach (var item in billList)
            {
                item.Accept(visitor);
            }
        }
    }
    
    class Program
    {
        static void Main(string[] args)
        {
            AccountBook accountBook = new AccountBook();

            //创建支出账单
            ConsumptionBill consumptionBill1 = new ConsumptionBill(10, "购买垃圾袋");
            ConsumptionBill consumptionBill2 = new ConsumptionBill(5000, "工资");

            //创建收入账单
            IncomeBill incomBill1 = new IncomeBill(50000, "项目");
            IncomeBill incomBill2 = new IncomeBill(20000, "项目维护费");

            accountBook.AddBill(consumptionBill1);
            accountBook.AddBill(consumptionBill2);
            accountBook.AddBill(incomBill1);
            accountBook.AddBill(incomBill2);

            //创建支出和收入访问者
            AccountBookVisitor boos = new BoosVisitor();
            AccountBookVisitor accounting = new AccountingVisitor();

            //老板查看账本
            accountBook.Show(boos);

            //会计查看账本
            accountBook.Show(accounting);

            Console.Read();
        }
    }

六、访问者模式的优缺点

优点:

        1、访问者模式使得添加新的操作变得简单。

        2、访问者模式使得有关的行为操作集中到一个访问者对象中,而不是分散到一个个的元素类中。

        3、访问者模式可以访问属于不同的等级结构的成员对象,而迭代只能访问属于同一个等级结构的成员对象。

缺点:

        1、增加新的元素变得困难。每增加一个新的元素都要在抽象访问者角色中添加一个新的抽象操作,那么具体的访问者角色也都要添加新的具体操作。

分享到: 网站分享代码

发表评论

快捷回复:

评论列表 (暂无评论,468人围观)参与讨论

还没有评论,来说两句吧...