什么是访问者模式?

一个对象有稳定的数据结构,却为不同的访问者提供不同的数据操作,对象提供接收访问者的方法,从而保证数据结构的稳定性和操作的多样性。也可以理解为,封装对象的操作方法,达到不改变对象数据结构的稳定性同时易于扩展操作。

解决的主要问题

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

如何实现

(1)Visitor接口:访问者接口,封装对象元素的操作,它定义了对每一个元素(Element)访问的行为,它的参数就是可以访问的元素,它的方法数理论上来讲与元素个数是一样的。

(2)Visitor1、Visitor2:访问者 -- 具体的访问类,它需要给出对每一个元素类访问时所产生的具体行为,如老板,会计。

(3)Element:元素对外访问入口接口,它定义了一个接受访问者的方法(Accept),其意义是指每一个元素(子类)都要可以被访问者访问。

(4)ElementA、ElementB:具体的元素类,它提供接受访问方法的具体实现,而这个具体的实现,通常情况下是使用访问者提供的访问该元素类的方法。

(5)Object:定义当中所说的对象结构,对象结构是一个抽象表述,它内部管理了元素集合,并且可以迭代这些元素供访问者访问
生活场景

公司有一个账本,抽象为一个对象,它有两个稳定的元素,一个是收入,一个是支出。公司有不同的角色都需要访问账本,抽象为访问者。比如。老板只关注总收入和总支出,会计关心每一笔收入是否缴税,每发一笔工资是否扣税。两者间是不同操作,且还有以后可能其他角色需要访问账本。

demo代码

1、Visitor接口

/**
* 访问者接口
* (1)查看收入账单
* (1)查看之处账单
*/
public interface IAccountBookViewer {
/**
* 消费账单
* @param bill
*/
void view(ConsumeBill bill); /**
* 收入账单
* @param bill
*/
void view(IncomeBill bill); }

IAccountBookViewer.java

2、访问者

2.1 具体类 --  老板

 /**
* 老板角色--查看账单
* 老板只关注总收入和总支出
*/
public class Boss implements IAccountBookViewer { /**
* 总消费
*/
private Double totalConsume = 0d;//默认值是null /**
* 总收入
*/
private Double totalIncome = 0d;//默认值是null public Double getTotalConsume() {
System.out.println("老板查看总支出:" + totalConsume);
return totalConsume;
} public Double getTotalIncome() {
System.out.println("老板查看总收入:" + totalIncome);
return totalIncome;
} /**
* 查看消费账单
*
* @param bill 消费账单
*/
@Override
public void view(ConsumeBill bill) {
totalConsume += bill.getAmount();
} /**
* 查看收入账单
*
* @param bill 账单
*/
@Override
public void view(IncomeBill bill) {
totalIncome += bill.getAmount();
}
}

Boss.java

2.2、具体类 -- 会计

 /**
* 注册会计师
* 关注具体收入和支出是否交税
*/
public class CPA implements IAccountBookViewer {
/**
* 查看支出,如果是工资,是否已经交税
*
* @param bill
*/
@Override
public void view(ConsumeBill bill) {
if (bill.getItem().equals("工资")) {
System.out.println("CPA查看是否工资已经扣税");
}
} /**
* 查看收入,所有收入都要交税
*
* @param bill
*/
@Override
public void view(IncomeBill bill) {
System.out.println("CPA查看所有收入是否已经缴税");
}
}

CPA.java

3、元素接口,定义每个元素行为 -- 对外提供accept方法,传入访问者

 /**
* 账单类接口,接收访问者
*/
public interface IBill {
void accept(IAccountBookViewer viewer);
}

IBill.java

4、具体的元素类

4.1、收入条目,可以理解为一个对象的元素

 /**
* 收入账单
*/
public class IncomeBill implements IBill {
/**
* 收入明细金额
*/
private Double amount; /**
* 收入条目
*/
private String item; /**
* 收入明细金额
*/
public Double getAmount() {
return amount;
} /**
* 收入条目
*/
public String getItem() {
return item;
} public IncomeBill(Double amount, String item) {
this.amount = amount;
this.item = item;
} @Override
public void accept(IAccountBookViewer viewer) {
viewer.view(this);
}
}

IncomeBill.java

4.2、支出条目,可以理解为一个对象元素

 /**
* 消费账单
*/
public class ConsumeBill implements IBill { /**
* 支出明细金额
*/
private Double amount; /**
* 支出条目
*/
private String item; /**
* 支出明细金额
*/
public Double getAmount() {
return amount;
} /**
* 支出条目
*/
public String getItem() {
return item;
} public ConsumeBill(Double amount, String item) {
this.amount = amount;
this.item = item;
} @Override
public void accept(IAccountBookViewer viewer) {
viewer.view(this);
}
}

ConsumeBill.java

5、对象类

包含具体元素的集合,提供访问集合元素的轮询方法show

 import java.util.ArrayList;
import java.util.List; /**
* 账本类
*/
public class AccountBook {
/**
* 账单条目列表
*/
private List<IBill> billList = new ArrayList<IBill>(); /**
* 增加账单条目
* @param bill 账单条目
*/
public void addBill(IBill bill){
billList.add(bill);
} /**
* 访问者产看账本
* @param viewer
*/
public void show(IAccountBookViewer viewer){
for (IBill bill : billList) {
bill.accept(viewer);
}
} }

AccountBook.java

测试入口

 public class App {
public static void (String[] args) {
//创建账本
AccountBook accBook = new AccountBook(); //收入条目
accBook.addBill(new IncomeBill(10000d, "广告收入"));
accBook.addBill(new IncomeBill(900000d, "房地产项目")); //支出条目
accBook.addBill(new ConsumeBill(20000d, "工资"));
accBook.addBill(new ConsumeBill(10000d, "办公室租金"));
accBook.addBill(new ConsumeBill(8000d, "水电费")); //访问者
Boss boss = new Boss();
CPA cpa = new CPA(); //访问者查看账单
accBook.show(boss);
accBook.show(cpa); boss.getTotalConsume();
boss.getTotalIncome();
}
}

main方法

输出结果

 CPA查看所有收入是否已经缴税
CPA查看所有收入是否已经缴税
CPA查看是否工资已经扣税
老板查看总支出:38000.0
老板查看总收入:910000.0

示例源码:https://github.com/LF20160912/pattern

设计模式Design Pattern(4) -- 访问者模式的更多相关文章

  1. 设计模式Design Pattern(1)--简介

    什么是设计模式? 软件开发人员在长期实践中总结出来的解决特定问题的一套解决方案. 对象设计原则 计模式主要是基于以下的面向对象设计原则. 对接口编程而不是对实现编程. 优先使用对象组合而不是继承. 设 ...

  2. C#设计模式之二十一访问者模式(Visitor Pattern)【行为型】

    一.引言 今天我们开始讲“行为型”设计模式的第九个模式,该模式是[访问者模式],英文名称是:Visitor Pattern.如果按老规矩,先从名称上来看看这个模式,我根本不能获得任何对理解该模式有用的 ...

  3. 设计模式(Design Pattern)系列之.NET专题

    最近,不是特别忙,重新翻了下设计模式,特地在此记录一下.会不定期更新本系列专题文章. 设计模式(Design pattern)是一套被反复使用.多数人知晓的.经过分类编目的.代码设计经验的总结. 使用 ...

  4. 设计模式23:Visitor 访问者模式(行为型模式)

    Visitor 访问者模式(行为型模式) 动机(Motivation)在软件构造过程中,由于需求的改变,某些类层次结构中常常需要增加新的行为(方法),如果直接在基类中做这样的修改,将会给子类带来繁重的 ...

  5. 设计模式-(13)访问者模式 (swift版)

    一,概念 访问者模式,是行为型设计模式之一.访问者模式是一种将数据操作与数据结构分离的设计模式,它可以算是 23 中设计模式中最复杂的一个,但它的使用频率并不是很高,大多数情况下,你并不需要使用访问者 ...

  6. 【design pattern】代理模式

    前言 设计模式分为三大类: 创建型模式:工厂方法模式.抽象工厂模式.单例模式.建造者模式.原型模式: 结构型模式:适配器模式.装饰器模式.代理模式.外观模式.桥接模式.组合模式.享元模式: 行为型模式 ...

  7. [Java复习] 设计模式 Design Pattern

    设计模式的六大原则 1.开闭原则(Open Close Principle) 对扩展开放,对修改关闭. 2.里氏代换原则(Liskov Substitution Principle) 任何基类可以出现 ...

  8. 【设计模式 - 24】之访问者模式(Visitor)

    1      模式简介 访问者模式的定义: 访问者模式将数据结构与数据操作进行了分离,解决了稳定的数据结构和易变的数据操作的耦合问题. 访问者模式的优点: 1)        符合单一职责原则: 2) ...

  9. 设计模式Design Pattern(3) -- 责任链模式

    什么是责任链模式? 责任链模式(Chain of Responsibility Pattern):请求知道公开接口,但不知道那个具体类处理,这些具体处理类对象连接成一条链.请求沿着这条链传递,直到有对 ...

随机推荐

  1. Ubantu下部署Flask项目安装与配置

    1.nginx 安装 sudo apt-get install nginx 启动,停止和重启 sudo /etc/init.d/nginx start sudo /etc/init.d/nginx s ...

  2. 按需加载controller——angular

    一.多视图应用 AngularJS 通过路由支持多视图应用, 可以根据路由动态加载所需的视图.随着视图的不断增加, js文件会越来越多, 而 AngularJS 默认需要把全部的 js 都一次性加载, ...

  3. epoll 性能分析(解决占用CPU 过高问题)

    针对自己写的一个服务器网络引擎Engine 文章后面附上源码 使用epoll  刚刚开始时候发现占用CPU 特别高,但是网络引擎里面基本没干什么事,不应该有这么高的CPU,一直不解, 于是自己慢慢的分 ...

  4. Java编程思想——标准 I / O

    将Syetem.out转换成PrintWriter 标准I/O重定向: 控制台信息量大,滚动快,查看困难 setIn(InputStream) setOut(...) setErr(...) 新I/O ...

  5. LeetCode.993-二叉树中的堂兄弟(Cousins in Binary Tree)

    这是悦乐书的第374次更新,第401篇原创 01 看题和准备 今天介绍的是LeetCode算法题中Easy级别的第235题(顺位题号是993).在二叉树中,根节点在深度0处,并且每个深度为k的节点的子 ...

  6. Visual Subst - 简单将任意文件夹挂载模拟成驱动器盘符硬盘分区的小工具

    随着电脑的使用,硬盘里的资料一天比一天多,也越来越杂乱.一些朋友为了方便文件管理,会考虑重新分区,让C.D.E等盘符分别担任不同的角色.不过,不分区的话也有一些小工具可以帮你实现. Visual Su ...

  7. 使用UI Automation实现自动化测试--1-4

    Introduction UI Automation是Microsoft .NET 3.0框架下提供的一种用于自动化测试的技术,是在MSAA基础上建立的,MSAA就是Microsoft Active ...

  8. python 并发编程 多线程 信号量

    一 信号量 信号量也是一把锁,可以指定信号量为5,对比互斥锁同一时间只能有一个任务抢到锁去执行,信号量同一时间可以有5个任务拿到锁去执行 如果说互斥锁是合租房屋的人去抢一个厕所,那么信号量就相当于一群 ...

  9. python 并发编程 多线程 目录

    线程理论 python 并发编程 多线程 开启线程的两种方式 python 并发编程 多线程与多进程的区别 python 并发编程 多线程 Thread对象的其他属性或方法 python 并发编程 多 ...

  10. mybatis+mysql 返回主键

    需求:使用MyBatis往MySQL数据库中插入一条记录后,需要返回该条记录的自增主键值. 方法:在mapper中指定keyProperty属性,示例如下: <insert id="i ...