1. 基本概念
在访问者模式(Visitor Pattern)中,我们使用了一个访问者类,它改变了元素类的执行算法。通过这种方式,元素的执行算法可以随着访问者改变而改变。这种类型的设计模式属于行为型模式。根据模式,元素对象已接受访问者对象,这样访问者对象就可以处理元素对象上的操作。
2. 基本实现思路
image.png
(1)Visitor:接口或者抽象类,它定义了对每一个元素(Element)访问的行为,它的参数就是可以访问的元素,它的方法数理论上来讲与元素个数是一样的,因此,访问者模式要求元素的类族要稳定,如果经常添加、移除元素类,必然会导致频繁地修改Visitor接口,如果这样则不适合使用访问者模式。
(2)ConcreteVisitor1、ConcreteVisitor2:具体的访问类,它需要给出对每一个元素类访问时所产生的具体行为。
(3)Element:元素接口或者抽象类,它定义了一个接受访问者的方法(Accept),其意义是指每一个元素都要可以被访问者访问。
(4)ConcreteElementA、ConcreteElementB:具体的元素类,它提供接受访问方法的具体实现,而这个具体的实现,通常情况下是使用访问者提供的访问该元素类的方法。
(5)ObjectStructure:定义当中所说的对象结构,对象结构是一个抽象表述,它内部管理了元素集合,并且可以迭代这些元素供访问者访问。
3. 代码示例
元素类
//单个单子的接口
public interface Bill {
void accept(FinanceOrderViewer viewer);
}
具体的元素类
// 消费的单子
class ConsumeBill implements Bill {
private double amount;
private String item;
public ConsumeBill(double amount, String item) {
super();
this.amount = amount;
this.item = item;
}
@Override
public void accept(FinanceOrderViewer 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;
}
@Override
public void accept(FinanceOrderViewer viewer) {
viewer.view(this);
}
public double getAmount() {
return amount;
}
public String getItem() {
return item;
}
}
对象结构
// 账本类
public class FinanceOrder {
// 单子列表
private List<Bill> billList = new ArrayList<>();
// 添加单子
public void addBill(Bill bill) {
billList.add(bill);
}
// 供账本的查看者查看账本
public void show(FinanceOrderViewer viewer) {
for (Bill bill : billList) {
bill.accept(viewer);
}
}
}
访问者接口
// 账单查看者接口(相当于Visitor)
public interface FinanceOrderViewer {
// 查看消费的单子
void view(ConsumeBill bill);
// 查看收入的单子
void view(IncomeBill bill);
}
具体访问者
//会计类
public class Accountant implements FinanceOrderViewer {
@Override
public void view(ConsumeBill bill) {
if (bill.getItem().equals("出口税费")) {
System.out.println("会计查看出口税费,可能有退税政策:" + bill.getAmount());
} else {
System.out.println("会计查看本次花费:" + bill.getAmount());
}
}
// 如果是收入,则所有的收入都要交税
@Override
public void view(IncomeBill bill) {
System.out.println("会计查看本次收入:" + bill.getAmount());
}
}
//经理类,查看账本的类之一
class Manager implements FinanceOrderViewer {
@Override
public void view(ConsumeBill bill) {
System.out.println("经理查看本次支出:"+ bill.getAmount());
}
@Override
public void view(IncomeBill bill) {
System.out.println("经理查看本次收入:" + bill.getAmount());
}
}
测试
class Test {
public static void main(String[] args) {
FinanceOrder accountBook = new FinanceOrder();
// 添加两条收入
accountBook.addBill(new IncomeBill(10000,"出口贸易"));
accountBook.addBill(new IncomeBill(12000,"租赁库房"));
// 添加两条支出
accountBook.addBill(new ConsumeBill(1000,"出口税费"));
accountBook.addBill(new ConsumeBill(2000,"材料费"));
FinanceOrderViewer manager = new Manager();
FinanceOrderViewer accountant = new Accountant();
// 两个访问者分别访问账本
accountBook.show(manager);
accountBook.show(accountant);
}
}
输出结果
经理查看本次收入:10000.0
经理查看本次收入:12000.0
经理查看本次支出:1000.0
经理查看本次支出:2000.0
会计查看本次收入:10000.0
会计查看本次收入:12000.0
会计查看出口税费,可能有退税政策:1000.0
会计查看本次花费:2000.0
4. 总结
优点:
- 稳定的数据结构,与可变的操作结构之间的解耦
- 增加访问者比较方便
缺点:
- 需要暴露元素对象类相关的状态及结构
- 增加元素类数据结构时,将会非常困难