访问者模式(Visitor):表示一个作用于某对象结构中的各元素的操作。它使你可以不在改变个元素类的前提下定义作用于这些元素的新操作。
一、访问者模式
在软件开发中,有时候我们对同一个对象可能会有不同的处理,对相同对象元素也存在不同的操作方式。比如,我们的银行账户,每个月都有收入和支出,不同的人(your girlFriend)去操作账户,有不同的操作方式。这就使用到了访问者模式。
访问者模式的目的是封装一些施加于某种数据结构元素之上的操作,一旦这些操作需要修改的话,接受这个操作的数据结构可以保持不变。为不同类型的元素提供多种访问操作方式,且可以在不修改原有系统的情况下增加新的操作方式。
访问者模式的UML图如下:
访问者模式由以下部分组成:
抽象访问者(Vistor):声明了一个或者多个方法操作,形成所有的具体访问者角色必须实现的接口。
具体访问者(ConcreteVisitor):实现Visitor声明的每一个操作,每一个操作实现算法的一部分。
抽象元素(Element):定义一个Accept操作,它以一个访问者为参数。
具体元素 (ConcreteElement):实现Accept操作。
对象结构(ObjectStructure):可以遍历结构中的所有元素,可以提供一个高层次的接口来允许访问者访问它的元素。
从上面的UML结构图中我们可以看出,访问者模式主要分为两个层次结构,一个是访问者层次结构,提供了抽象访问者和具体访问者,主要用于一些操作。一个是元素层次结构,提供了抽象元素和具体元素,主要用于声明Accept操作。
二、案例分析
我们用代码来写一写开篇的例子,账户有支出和收入,参与账户的既有”我”,又有女友,最后通过统计账单的信息,了解整个账户的变化。
抽象元素:Bill
// 账户接口
interface Bill {
void accept(AccountBookView viewer);
}
具体元素:ConsumerBill 和 IncomeBill
// 支出类
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 void setItem(String item) {
this.item = item;
}
public double getAmount() {
return amount;
}
public void setAmount(double amount) {
this.amount = amount;
}
}
// 收入
class IncomeBill implements Bill {
private String item;
private double amount;
public IncomeBill(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 void setItem(String item) {
this.item = item;
}
public double getAmount() {
return amount;
}
public void setAmount(double amount) {
this.amount = amount;
}
}
抽象访问者 AccountBookView :
// 访问者接口
interface AccountBookView {
void view(ConsumerBill consumerBill);
void view(IncomeBill incomeBill);
}
具体访问者:Boy 和 Girl
class Boy implements AccountBookView {
private double totalConsumer;
private double totalIncome;
@Override
public void view(ConsumerBill consumerBill) {
totalConsumer += consumerBill.getAmount();
}
@Override
public void view(IncomeBill incomeBill) {
totalIncome += incomeBill.getAmount();
}
public void getTotalConsumer() {
System.out.println("男朋友一共消费了" + totalConsumer+"元");
}
public void getTotalIncome() {
System.out.println("男朋友一共收入了" + totalIncome+"元");
}
}
//女友
class Girl implements AccountBookView {
private int count = 0;
@Override
public void view(ConsumerBill consumerBill) {
count++;
if(consumerBill.getItem().equals("支出")){
System.out.println("第"+count+"笔支出,消费了"+consumerBill.getAmount()+"元");
}
}
@Override
public void view(IncomeBill incomeBill) {
if(incomeBill.getItem().equals("收入")){
System.out.println("第"+count+"笔收入,收入了"+incomeBill.getAmount()+"元");
}
}
}
对象结构:AccountBook
// 账单类
class AccountBook{
private List<Bill> list = new ArrayList<>();
public void add(Bill bill){
list.add(bill);
}
public void show(AccountBookView view){
for(Bill b :list){
b.accept(view);
}
}
}
客户端调用:
public class Test {
public static void main(String[] args) {
//消费情况
Bill consumer1 = new ConsumerBill("支出",1000);
Bill income1 = new IncomeBill("收入",1000);
Bill consumer2 = new ConsumerBill("支出",5000);
Bill income2 = new IncomeBill("收入",10000);
// 添加账单
AccountBook ab =new AccountBook();
ab.add(consumer1);
ab.add(income1);
ab.add(consumer2);
ab.add(income2);
//访问者
AccountBookView boy = new Boy();
AccountBookView girl=new Girl();
ab.show(boy);
ab.show(girl);
((Boy)boy).getTotalConsumer();
((Boy)boy).getTotalIncome();
}
}
运行结果(代码亲测,可以通过):
代码地址:Vistor
三、 模式结语
访问者模式的优点:
使得新增新的访问操作变得更加简单。
能够使得用户在不修改现有类的层次结构下,定义该类层次结构的操作。
将有关元素对象的访问行为集中到一个访问者对象中,而不是分散搞一个个的元素类中。
访问者模式的使用场景:
对象结构中对象对应的类很少改变,但经常需要在此对象结构上定义新的操作。
需要对一个对象结构中的对象进行很多不同的并且不相关的操作,而需要避免让这些操作“污染”这些对象的类,也不希望在增加新操作时修改这些类。
欢迎大家评论留言,点击查看更多设计模式。