GOF设计模式-对象行为型模式-访问者模式

26 篇文章 0 订阅
20 篇文章 0 订阅

操作复杂对象结构——访问者模式

 

访问者模式概述

访问者模式是一种较为复杂的行为型设计模式,它包含访问者和被访问元素两个主要组成部 分,这些被访问的元素通常具有不同的类型,且不同的访问者可以对它们进行不同的访问操 作。例如处方单中的各种药品信息就是被访问的元素,而划价人员和药房工作人员就是访问 者。访问者模式使得用户可以在不修改现有系统的情况下扩展系统的功能,为这些不同类型 的元素增加新的操作。
在使用访问者模式时,被访问元素通常不是单独存在的,它们存储在一个集合中,这个集合 被称为“对象结构”,访问者通过遍历对象结构实现对其中存储的元素的逐个操作。
访问者模式定义如下:
访问者模式(Visitor Pattern):提供一个作用于某对象结构中的各元素的操作表示,它使我们可以 在不改变各元素的类的前提下定义作用于这些元素的新操作。访问者模式是一种对象行为型 模式。

访问者模式的结构较为复杂,其结构如图:

 

在访问者模式结构图中包含如下几个角色:
●Vistor(抽象访问者):抽象访问者为对象结构中每一个具体元素类ConcreteElement声明一 个访问操作,从这个操作的名称或参数类型可以清楚知道需要访问的具体元素的类型,具体 访问者需要实现这些操作方法,定义对这些元素的访问操作。
●ConcreteVisitor(具体访问者):具体访问者实现了每个由抽象访问者声明的操作,每一个操 作用于访问对象结构中一种类型的元素。
●Element(抽象元素):抽象元素一般是抽象类或者接口,它定义一个accept()方法,该方法 通常以一个抽象访问者作为参数。【稍后将介绍为什么要这样设计。】
●ConcreteElement(具体元素):具体元素实现了accept()方法,在accept()方法中调用访问者 的访问方法以便完成对一个元素的操作。
● ObjectStructure(对象结构):对象结构是一个元素的集合,它用于存放元素对象,并且提 供了遍历其内部元素的方法。它可以结合组合模式来实现,也可以是一个简单的集合对象, 如一个List对象或一个Set对象。
访问者模式中对象结构存储了不同类型的元素对象,以供不同访问者访问。访问者模式包括 两个层次结构,一个是访问者层次结构,提供了抽象访问者和具体访问者,一个是元素层次 结构,提供了抽象元素和具体元素。相同的访问者可以以不同的方式访问不同的元素,相同 的元素可以接受不同访问者以不同访问方式访问。在访问者模式中,增加新的访问者无须修 改原有系统,系统具有较好的可扩展性。

 

访问者模式有两个特点

  1. 一般被访问的东西所持有的方法是固定的,就像账单只有收入和支出两个功能。而访问者是不固定的。

  2. 数据操作与数据结构相分离:频繁的更改数据,但不结构不变。比如:虽然每一天账单的数据都会变化(数据变化),但是只有两类数据,就是支出和收入(结构不变)。

简化如下图:

 

例子:

账单接口:

/**
 * 创建一个账单接口,有接收访问者的功能
 * 
 * @author hgx
 *
 */
interface Bill {
    void accept(AccountBookView viewer);
}

 

消费类:

class ConsumerBill implements Bill {

    private String item;
    private double amount;

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

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

    public String getItem() {
        return item;
    }

    public double getAmount() {
        return amount;
    }

}

收入类:

class IncomeBill implements Bill {

    private String item;
    private double amount;

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

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

    public String getItem() {
        return item;
    }

    public double getAmount() {
        return amount;
    }

}


访问者接口:

interface AccountBookView {
    // 查看消费的单子
    void view(ConsumerBill consumerBill);

    // 查看收入单子
    void view(IncomeBill incomeBill);
}

 

老板类:

// 老板类:访问者是老板,主要查看总支出和总收入
class Boss implements AccountBookView {

    private double totalConsumer;
    private double totalIncome;

    // 查看消费的单子
    public void view(ConsumerBill consumerBill) {
        totalConsumer = totalConsumer + consumerBill.getAmount();
    }

    // 查看收入单子
    public void view(IncomeBill incomeBill) {
        totalIncome = totalIncome + incomeBill.getAmount();
    }

    public void getTotalConsumer() {
        System.out.println("老板一共消费了" + totalConsumer);
    }

    public void getTotalIncome() {
        System.out.println("老板一共收入了" + totalIncome);
    }
}

 

会计类:

/**
 * 会计类:访问者是会计,主要记录每笔单子
 *
 * @author hgx
 */

class CPA implements AccountBookView {

    int count = 0;

    // 查看消费的单子
    public void view(ConsumerBill consumerBill) {
        count++;
        if (consumerBill.getItem().equals("消费")) {
            System.out.println("第" + count + "个单子消费了:" + consumerBill.getAmount());
        }
    }
    // 查看收入单子

    public void view(IncomeBill incomeBill) {

        if (incomeBill.getItem().equals("收入")) {
            System.out.println("第" + count + "个单子收入了:" + incomeBill.getAmount());
        }
    }

}

账单类:

/**
 * 账单类:用于添加账单,和为每一个账单添加访问者
 *
 * @author hgx
 *
 */
class AccountBook {

    private List<Bill> listBill = new ArrayList<Bill>();

    // 添加单子
    public void add(Bill bill) {
        listBill.add(bill);
    }

    // 为每个账单添加访问者
    public void show(AccountBookView viewer) {
        for (Bill b : listBill) {
            b.accept(viewer);
        }
    }
}

测试类:

/*
 *测试类
 */
public class Test {

    public static void main(String[] args) {
        // 创建消费和收入单子
        Bill consumerBill = new ConsumerBill("消费", 3000);
        Bill incomeBill = new IncomeBill("收入", 5000);
        Bill consumerBill2 = new ConsumerBill("消费", 4000);
        Bill incomeBill2 = new IncomeBill("收入", 8000);
        // 添加单子
        AccountBook accountBook = new AccountBook();
        accountBook.add(consumerBill);
        accountBook.add(incomeBill);
        accountBook.add(consumerBill2);
        accountBook.add(incomeBill2);
        // 创建访问者
        Boss boss = new Boss();
        CPA cpa = new CPA();

        // 接受访问者
        accountBook.show(boss);
        accountBook.show(cpa);
        // boss查看总收入和总消费
        boss.getTotalConsumer();
        boss.getTotalIncome();

    }

}

测试结果:

 

 

访问者模式总结

由于访问者模式的使用条件较为苛刻,本身结构也较为复杂,因此在实际应用中使用频率不是特别高。当系统中存在一个较为复杂的对象结构,且不同访问者对其所采取的操作也不相 同时,可以考虑使用访问者模式进行设计。在XML文档解析、编译器的设计、复杂集合对象 的处理等领域访问者模式得到了一定的应用。

1.主要优点
访问者模式的主要优点如下:
(1) 增加新的访问操作很方便。使用访问者模式,增加新的访问操作就意味着增加一个新的具 体访问者类,实现简单,无须修改源代码,符合“开闭原则”。
(2) 将有关元素对象的访问行为集中到一个访问者对象中,而不是分散在一个个的元素类中。 类的职责更加清晰,有利于对象结构中元素对象的复用,相同的对象结构可以供多个不同的 访问者访问。
(3) 让用户能够在不修改现有元素类层次结构的情况下,定义作用于该层次结构的操作。
2.主要缺点
访问者模式的主要缺点如下:
(1) 增加新的元素类很困难。在访问者模式中,每增加一个新的元素类都意味着要在抽象访问 者角色中增加一个新的抽象操作,并在每一个具体访问者类中增加相应的具体操作,这违背 了“开闭原则”的要求。
(2) 破坏封装。访问者模式要求访问者对象访问并调用每一个元素对象的操作,这意味着元素 对象有时候必须暴露一些自己的内部操作和内部状态,否则无法供访问者访问。
3.适用场景
在以下情况下可以考虑使用访问者模式:
(1) 一个对象结构包含多个类型的对象,希望对这些对象实施一些依赖其具体类型的操作。在 访问者中针对每一种具体的类型都提供了一个访问操作,不同类型的对象可以有不同的访问 操作。
(2) 需要对一个对象结构中的对象进行很多不同的并且不相关的操作,而需要避免让这些操 作“污染”这些对象的类,也不希望在增加新操作时修改这些类。访问者模式使得我们可以将相 关的访问操作集中起来定义在访问者类中,对象结构可以被多个不同的访问者类所使用,将 对象本身与对象的访问操作分离。
(3) 对象结构中对象对应的类很少改变,但经常需要在此对象结构上定义新的操作。

 

 

 

 

 

 

 

 

 

 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值