设计模式(行为型设计模式——访问者模式)
访问者模式
基本定义
将作用于某种数据结构中的各元素的操作分离出来封装成独立的类,使其在不改变数据结构的前提下可以添加作用于这些元素的新的操作,为数据结构中的每个元素提供多种访问方式。它将对数据的操作与数据结构进行分离,是行为类模式中最复杂的一种模式。
模式结构
- Vistor 抽象访问者:为该对象结构中的ConcreteElement的每一个类声明的一个操作。
- ConcreteVisitor 具体访问者:实现Visitor申明的每一个操作,每一个操作实现算法的一部分。
- Element 抽象元素:定义一个Accept操作,它以一个访问者为参数。
- ConcreteElement 具体元素 :实现Accept操作。
- ObjectStructure 对象结构:能够枚举它的元素,可以提供一个高层的接口来允许访问者访问它的元素。
代码实现
Vistor 抽象访问者
/**
* @Description 抽象访问者 Vistor
* vistor中方法的个数,直接对应元素具体类的子数
**/
public interface Vistor {
//支出账单
void view(ExpenseBillElement expenseBillElement);
//收入账单
void view(IncomeBillElement incomeBillElement);
}
ConcreteVisitor 具体访问者
//访问者BOSS:查看收入和支出总和
@Slf4j
public class BossVistor implements Vistor {
double totalExpenses;//总支出
double totalIncome;//总收入
public double getTotalExpenses() {
log.info("老板查看总支出: {} RMB", this.totalExpenses);
return totalExpenses;
}
public double getTotalIncome() {
log.info("老板查看总收入: {} RMB", this.totalIncome);
return totalIncome;
}
/**
* 查看支出
* @param expenseBillElement
*/
@Override
public void view(ExpenseBillElement expenseBillElement) {
totalExpenses += expenseBillElement.getAmount();
}
/**
* 查看收入
* @param incomeBillElement
*/
@Override
public void view(IncomeBillElement incomeBillElement) {
totalIncome += incomeBillElement.getAmount();
}
}
//访问者财务人员:查看每一笔收入详情
@Slf4j
public class FinanceVistor implements Vistor {
@Override
public void view(ExpenseBillElement expenseBillElement) {
log.info("财务查看支出栏目 :{} , 支出金额:{}", expenseBillElement.getItem(), expenseBillElement.getAmount());
}
@Override
public void view(IncomeBillElement incomeBillElement) {
log.info("财务查看收入栏目 :{} , 支出金额:{}", incomeBillElement.getItem(), incomeBillElement.getAmount());
}
}
Element 抽象元素
/**
* 账单
* 提供接受访问的方法
**/
public interface BillElement {
void accept(Vistor vistor);
}
ConcreteElement 具体元素
//支付账单元素
public class ExpenseBillElement implements BillElement {
double amount;
String item;
public double getAmount() {
return amount;
}
public void setAmount(double amount) {
this.amount = amount;
}
public String getItem() {
return item;
}
public void setItem(String item) {
this.item = item;
}
public ExpenseBillElement(double amount, String item) {
this.amount = amount;
this.item = item;
}
@Override
public void accept(Vistor vistor) {
vistor.view(this);
}
}
//收入账单元素
public class IncomeBillElement implements BillElement {
double amount;
String item;
public double getAmount() {
return amount;
}
public String getItem() {
return item;
}
public IncomeBillElement(double amount, String item) {
this.amount = amount;
this.item = item;
}
@Override
public void accept(Vistor vistor) {
vistor.view(this);
}
}
ObjectStructure 对象结构
public class BillBook {
private List<BillElement> billElementList = new ArrayList<>();
public void addBill(BillElement billElement){
billElementList.add(billElement);
}
/**
*
* 1、所有访问者都是通过show方法进行最终访问,也就是说谁要访问对象,
* 都要经过show方法,将访问者传入show方法
*
* 2、通过账单循环迭代,可以将所有元素一一列举,
* 如果对于一些访问者的目的, 可以通过自己的类,对元素进行检索
*
* @param vistor
*/
public void show(Vistor vistor){
billElementList.stream().forEach(billElement -> {
billElement.accept(vistor);
});
}
}
测试类
public class Test {
public static void main(String[] args) {
BillBook billBook = new BillBook();
billBook.addBill(new ExpenseBillElement(1000d, "工资"));
billBook.addBill(new ExpenseBillElement(2000d, "买材料"));
billBook.addBill(new IncomeBillElement(3000d, "卖产品"));
billBook.addBill(new IncomeBillElement(1000d, "做广告"));
BossVistor bossVistor = new BossVistor();
billBook.show(bossVistor);
bossVistor.getTotalExpenses();
bossVistor.getTotalIncome();
FinanceVistor financeVistor = new FinanceVistor();
billBook.show(financeVistor);
}
}
执行结果:
BossVistor - 老板查看总支出: 3000.0 RMB
BossVistor - 老板查看总收入: 4000.0 RMB
FinanceVistor - 财务查看支出栏目 :工资 , 支出金额:1000.0
FinanceVistor - 财务查看支出栏目 :买材料 , 支出金额:2000.0
FinanceVistor - 财务查看收入栏目 :卖产品 , 支出金额:3000.0
FinanceVistor - 财务查看收入栏目 :做广告 , 支出金额:1000.0
优点
高扩展。能够在不修改对象结构中的元素的情况下,为对象结构中的元素添加新的功能。
高复用。可以通过访问者来定义整个对象结构通用的功能,从而提高系统的复用程度。
松耦合。访问者模式将数据结构与作用于结构上的操作解耦,使得操作集合可相对自由地演化而不影响系统的数据结构。
符合单一职责原则。访问者模式把相关的行为封装在一起,构成一个访问者,使每一个访问者的功能都比较单一。
缺点
增加新的元素类很困难。在访问者模式中,每增加一个新的元素类,都要在每一个具体访问者类中增加相应的具体操作,这违背了“开闭原则”。
破坏封装。访问者模式中具体元素对访问者公布细节,这破坏了对象的封装性。
违反了依赖倒置原则。访问者模式依赖了具体类,而没有依赖抽象类。
使用场景
- 对象结构中对象对应的类很少改变,但经常需要在此对象结构上定义新的操作。
- 需要对一个对象结构中的对象进行很多不同的并且不相关的操作,而需要避免让这些操作“污染”这些对象的类,也不希望在增加新操作时修改这些类。
总结
- 访问者模式封装了对象结构元素之上的操作,使得新增元素的操作变得非常简单。所以它比较适用于那么对象结构很少变化的类。
- 访问者模式中对象结构存储了不同类型的元素对象,以供不同访问者访问。