访问者模式

定义:


表示一个作用于某对象结构中的各元素的操作。它使你可以在不改变各元素类的前提下定义作用于这些元素的新操作。


举例:(记账例子):

账本只有收入和消费类型,而访问者有多种,如老板和会计。

/**
* 账单接口,有接受访问者的功能
*/
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;
}

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

public String getItem() {
return item;
}

public double getAmount() {
return amount;
}
}

/*
*支出账单
*/
class IncomingBill implements Bill {
private String item;
private double amount;

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

@Override
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(IncomingBill incomingBill);
}


/**
*访问者老板
*/
class Boss implements AccountBookView {

private double totalConsumer;
private double totalIncome;


@Override
public void view(ConsumerBill consumerBill) {
totalConsumer = totalConsumer + consumerBill.getAmount();
}

@Override
public void view(IncomingBill incomingBill) {
totalIncome = totalIncome + incomingBill.getAmount();
}

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

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

}

/**
*访问者会计
*/
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(IncomingBill incomeBill) {

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

}

/**
*账单类:用于添加账单,和为每一个账单添加访问者
*/
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 Client{

public void main(String[] args) {

// 创建消费和收入单子
Bill consumerBill = new ConsumerBill("消费", 3000);
Bill incomeBill = new IncomingBill("收入", 5000);
Bill consumerBill2 = new ConsumerBill("消费", 4000);
Bill incomeBill2 = new IncomingBill("收入", 8000);

// 添加单子
AccountBook accountBook = new AccountBook();
accountBook.add(consumerBill);
accountBook.add(incomeBill);
accountBook.add(consumerBill2);
accountBook.add(incomeBill2);

// 创建访问者
AccountBookView boss = new Boss();
AccountBookView cpa = new CPA();

// 接受访问者
accountBook.show(boss);
accountBook.show(cpa);

// boss查看总收入和总消费
((Boss) boss).getTotalConsumer();
((Boss) boss).getTotalIncome();
}
}

总结:

意图:​主要将数据结构与数据操作分离。

主要解决:​稳定的数据结构和易变的操作耦合问题。

何时使用:​需要对一个对象结构中的对象进行很多不同的并且不相关的操作,而需要避免让这些操作”污染”这些对象的类,使用访问者模式将这些封装到类中。

如何解决:​在被访问的类里面加一个对外提供接待访问者的接口。

关键代码:​在数据基础类里面有一个方法接受访问者,将自身引用传入访问者。

优点:​ 1、符合单一职责原则。 2、优秀的扩展性。 3、灵活性。

缺点:​ 1、具体元素对访问者公布细节,违反了迪米特原则。 2、具体元素变更比较困难。 3、违反了依赖倒置原则,依赖了具体类,没有依赖抽象。

使用场景:

1、对象结构中对象对应的类很少改变,但经常需要在此对象结构上定义新的操作。

2、需要对一个对象结构中的对象进行很多不同的并且不相关的操作,而需要避免让这些操作”污染”这些对象的类,也不希望在增加新操作时修改这些类。

注意事项:​访问者可以对功能进行统一,可以做报表、UI、拦截器与过滤器。