设计模式学习总结01----访问者模式

            场景

        描述:访问者多变,电脑里的结构(数据结构不变),操作不影响电脑内部结构;

1.       账本 有固定的 支出和收入;

            老板 查看 账本的 总支出和总收入

            会计 查看 账本的  收入为工资时是否纳税;

        使用总结:账本的内部数据构成不变,支出和收入;根据访问者老板,会计的不同,他们俩的行为-查看账本 的目的和行为        不同。查看不改变账本的内容,再增加一个访问者很容易;

    2.  电脑 有鼠标 键盘 等固定结构的 元素;

        不同的使用者 访问电脑 做不同的事,但是不改变电脑的 结构,元素;

        实现:

(1)Visitor:接口或者抽象类,它定义了对每一个元素(Element)访问的行为,它的参数就是可以访问的元素,它的方法数理论上来讲与元素个数是一样的,因此,访问者模式要求元素的类族要稳定,如果经常添加、移除元素类,必然会导致频繁地修改Visitor接口,如果这样则不适合使用访问者模式。

(2)ConcreteVisitor1、ConcreteVisitor2:具体的访问类,它需要给出对每一个元素类访问时所产生的具体行为。

(3)Element:元素接口或者抽象类,它定义了一个接受访问者的方法(Accept),其意义是指每一个元素都要可以被访问者访问。

(4)ConcreteElementA、ConcreteElementB:具体的元素类,它提供接受访问方法的具体实现,而这个具体的实现,通常情况下是使用访问者提供的访问该元素类的方法。

(5)ObjectStructure:定义当中所说的对象结构,对象结构是一个抽象表述,它内部管理了元素集合,并且可以迭代这些元素供访问者访问。

访问者模式的简单例子

我们都知道财务都是有账本的,这个账本就可以作为一个对象结构,而它其中的元素有两种,收入和支出,这满足我们访问者模式的要求,即元素的个数是稳定的,因为账本中的元素只能是收入和支出。

而查看账本的人可能有这样几种,比如老板,会计事务所的注会,财务主管,等等。而这些人在看账本的时候显然目的和行为是不同的。

首先我们给出单子的接口,它只有一个方法accept。

//单个单子的接口(相当于Element)
public interface Bill {

    void accept(AccountBookViewer viewer);

}
  • 1

其中的方法参数AccountBookViewer是一个账本访问者接口,接下来也就是实现类,收入单子和消费单子,或者说收入和支出类。

//消费的单子
public class ConsumeBill implements Bill{

    private double amount;

    private String item;

    public ConsumeBill(double amount, String item) {
        super();
        this.amount = amount;
        this.item = item;
    }

    public void accept(AccountBookViewer viewer) {
        viewer.view(this);
    }

    public double getAmount() {
        return amount;
    }

    public String getItem() {
        return item;
    }

}
//收入单子
public class IncomeBill implements Bill{

    private double amount;

    private String item;

    public IncomeBill(double amount, String item) {
        super();
        this.amount = amount;
        this.item = item;
    }

    public void accept(AccountBookViewer viewer) {
        viewer.view(this);
    }

    public double getAmount() {
        return amount;
    }

    public String getItem() {
        return item;
    }

}
  • 1

上面最关键的还是里面的accept方法,它直接让访问者访问自己,这相当于一次静态分派(文章最后进行解释),当然我们也可以不使用重载而直接给方法不同的名称。

接下来是账本访问者接口

//账单查看者接口(相当于Visitor)
public interface AccountBookViewer {

    //查看消费的单子
    void view(ConsumeBill bill);

    //查看收入的单子
    void view(IncomeBill bill);

}
  • 1

这两个方法是重载方法,就是在上面的元素类当中用到的,当然你也可以按照访问者模式类图当中的方式去做,将两个方法分别命名为viewConsumeBill和viewIncomeBill,而一般建议按照类图上来做的

访问者的实现

//老板类,查看账本的类之一
public class Boss implements AccountBookViewer{

    private double totalIncome;

    private double totalConsume;

    //老板只关注一共花了多少钱以及一共收入多少钱,其余并不关心
    public void view(ConsumeBill bill) {
        totalConsume += bill.getAmount();
    }

    public void view(IncomeBill bill) {
        totalIncome += bill.getAmount();
    }

    public double getTotalIncome() {
        System.out.println("老板查看一共收入多少,数目是:" + totalIncome);
        return totalIncome;
    }

    public double getTotalConsume() {
        System.out.println("老板查看一共花费多少,数目是:" + totalConsume);
        return totalConsume;
    }

}
  • 1
//注册会计师类,查看账本的类之一
public class CPA implements AccountBookViewer{

    //注会在看账本时,如果是支出,则如果支出是工资,则需要看应该交的税交了没
    public void view(ConsumeBill bill) {
        if (bill.getItem().equals("工资")) {
            System.out.println("注会查看工资是否交个人所得税。");
        }
    }
    //如果是收入,则所有的收入都要交税
    public void view(IncomeBill bill) {
        System.out.println("注会查看收入交税了没。");
    }

}
  • 1

老板只关心收入和支出的总额,而注会只关注该交税的是否交税

接下来是账本类,它是当前访问者模式例子中的对象结构

//账本类(相当于ObjectStruture)
public class AccountBook {
    //单子列表
    private List<Bill> billList = new ArrayList<Bill>();
    //添加单子
    public void addBill(Bill bill){
        billList.add(bill);
    }
    //供账本的查看者查看账本
    public void show(AccountBookViewer viewer){
        for (Bill bill : billList) {
            bill.accept(viewer);
        }
    }
}
  • 1

账本类当中有一个列表,这个列表是元素(Bill)的集合,这便是对象结构的通常表示,它一般会是一堆元素的集合,不过这个集合不一定是列表,也可能是树,链表等等任何数据结构,甚至是若干个数据结构。其中show方法,就是账本类的精髓,它会枚举每一个元素,让访问者访问。

测试客户端

public class Client {

    public static void main(String[] args) {
        AccountBook accountBook = new AccountBook();
        //添加两条收入
        accountBook.addBill(new IncomeBill(10000, "卖商品"));
        accountBook.addBill(new IncomeBill(12000, "卖广告位"));
        //添加两条支出
        accountBook.addBill(new ConsumeBill(1000, "工资"));
        accountBook.addBill(new ConsumeBill(2000, "材料费"));

        AccountBookViewer boss = new Boss();
        AccountBookViewer cpa = new CPA();

        //两个访问者分别访问账本
        accountBook.show(cpa);
        accountBook.show(boss);

        ((Boss) boss).getTotalConsume();
        ((Boss) boss).getTotalIncome();
    }
}
  • 1

上面的代码中,可以这么理解,账本以及账本中的元素是非常稳定的,这些几乎不可能改变,而最容易改变的就是访问者这部分。

访问者模式最大的优点就是增加访问者非常容易,我们从代码上来看,如果要增加一个访问者,你只需要做一件事即可,那就是写一个类,实现AccountBookViewer接口,然后就可以直接调用AccountBook的show方法去访问账本了。

如果没使用访问者模式,一定会增加许多if else,而且每增加一个访问者,你都需要改你的if else,代码会显得非常臃肿,而且非常难以扩展和维护。




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值