浅谈设计模式-visitor访问者模式
先看一个和visitor无关的案例.假设你现在有一个书架,这个书架有两种操作,1添加书籍2阅读每一本书籍的简介.
- //书架
public class Bookcase {- List<Book> structure = new ArrayList();
- public void add(Book book) {
- structure.add(book);
- }
- //查看书籍的简介
- public void readIntroduction() {
- for (Book book : structure) {
- if (book.getClass() == BookA.class) {
- BookA bookA = (BookA) book;
- bookA.IntroductionA();
- } else if (book.getClass() == BookB.class) {
- BookB bookB = (BookB) book;
- bookB.IntroductionB();
- } else {
- BookC bookC = (BookC) book;
- bookC.IntroductionC();
- }
- }
- }
- }
- //书籍接口
- public interface Book {
- }
- public class BookA implements Book{
- //A书的简介
- public void IntroductionA(){
- System.out.println("我是一本神奇的书AAA");
- }
- }
- public class BookB implements Book{
- //B书的简介
- public void IntroductionB(){
- System.out.println("我是一本神奇的书BBB");
- }
- }
- public class BookC implements Book{
- //C书的简介
- public void IntroductionC(){
- System.out.println("我是一本神奇的书CCC");
- }
- }
- //客户端
- public class Client {
- public static void main(String[] args) {
- Bookcase bookcase = new Bookcase();
- bookcase.add(new BookA());
- bookcase.add(new BookB());
- bookcase.add(new BookC());
- bookcase.readIntroduction();
- //我是一本神奇的书AAA
//我是一本神奇的书BBB
//我是一本神奇的书CCC- }
- }
每一本书籍的介绍都是不一样的方法.为了查看书籍的介绍,我们不得不在循环中做类型的判断书籍的类型,然后做强制类型转换.(注意!!!以上代码为了演示出现的问题,所以将每本书的介绍
放到了不同的方法内)
下面引入visitor设计模式解决这个问题.
首先引入一个抽象的访问者接口,该接口有已知需要访问的书籍的访问方式.
- //访问者接口
- public interface Visitor {
- public void read(BookA bookA);
- public void read(BookB bookB);
- public void read(BookC bookC);
- }
在引入一个具体访问者类,该类需要实现访问者接口中的所有方法.
- public class VisitorImpl implements Visitor {
- public void read(BookA bookA){
- bookA.IntroductionA();
- }
- public void read(BookB bookB){
- bookB.IntroductionB();
- }
- public void read(BookC bookC){
- bookC.IntroductionC();
- }
- }
在原有的书籍接口中抽象一个方法,该方法接收一个具体访问者对象.
- //书籍接口
- public interface Book {
- public void accept(Visitor visitor);
- }
接下来让每本具体的书都重写接口中的方法.
- public class BookA implements Book{
- public void accept(Visitor visitor){
- visitor.read(this);
- }
- //A书的简介
- public void IntroductionA(){
- System.out.println("我是一本神奇的书AAA");
- }
- }
- public class BookB implements Book{
- public void accept(Visitor visitor){
- visitor.read(this);
- }
- //B书的简介
- public void IntroductionB(){
- System.out.println("我是一本神奇的书BBB");
- }
- }
- public class BookC implements Book{
- public void accept(Visitor visitor){
- visitor.read(this);
- }
- //C书的简介
- public void IntroductionC(){
- System.out.println("我是一本神奇的书CCC");
- }
- }
可以看出,每一本书都实现了accept方法,每个方法内调用visitor的read方法,并且把自身传入.接下来看书架类做了哪些修改.
- public class Bookcase {
- List<Book> structure = new ArrayList();
- private Visitor visitor;
- //创建书架,需要给一个访问者
- public Bookcase(Visitor visitor) {
- this.visitor = visitor;
- }
- public void add(Book book) {
- structure.add(book);
- }
- //调用每本书的分派方法.
- public void readIntroduction() {
- for (Book book : structure) {
- book.accept(visitor);
- }
- }
- }
最终使用客户端调用书架方法.
- //客户端
- public class Client {
- public static void main(String[] args) {
- //具体访问者对象
- Visitor visitor = new VisitorImpl();
- Bookcase bookcase = new Bookcase(visitor);
- bookcase.add(new BookA());
- bookcase.add(new BookB());
- bookcase.add(new BookC());
- bookcase.readIntroduction();
//我是一本神奇的书AAA
//我是一本神奇的书BBB
//我是一本神奇的书CCC- }
- }
大概缕一下调用过程.首先创建一个书架需要传入一个具体访问者对象.当书架调用查询所有书籍介绍时,首先是A书籍,A书籍调用自身的委托功能,将自身传递给具体访问者对象,并且调用具体
访问者对象的read()方法.如此具体方法着触发 参数为BookA的重载方法,调用A书籍自身的介绍方法.这是一个双重分派过程.
访问者模式中的角色
1 抽象访问者角色:抽象访问者角色必须定义出所有已知节点.(Visitor)
2 具体访问者角色:实现所有抽象角色的方法,调用每个方法调用节点自身的访问功能.(VisitorImpl)
3 抽象节点角色:抽象节点角色定义出一个分配功能,将书籍自身的访问方法,委托给访问者角色.(Book)
4 具体节点角色:定义自身的业务逻辑,实现接口中的委托方法,将自身传递给访问者角色(BookA,BookB,BookC)
5 结构对象角色:定义了存放操作具体角色的类,对具体角色进行增加,查询.(Bookcase)
访问者模式中的优点和缺点
首先谈一下自己的拙见,然后咱们在看书中的标准描述:
优点:对把数据和操作分开了,例子中的书籍其实就是数据,每本书都有自己的介绍功能,而具体访问者则负责把所有书籍的介绍功能统统拿过来,由自己管理.
一个访问者可以访问全部的节点书籍的功能.
缺点:如果增加一个新的节点,意味着要在抽象访问者角色中添加新的访问方式,还要重写所有的具体访问节点.
书中的总结(摘抄自--Java与模式)
优点:
1 访问者模式使得增加新的操作变得很容易.如果一些操作依赖于一个负责的结
构对象的话,那么一般而言,新增的操作会很复杂.而使用访问者,增加新的操作
就意味着新增加一个新的访问者类,因此,变得很容易.
2 访问者模式将有关的行为集中到一个访问者对象中,而不是分散到一个个的节
点类中.
3 访问者模式可以跨过几个类的等级结构访问属于不同的等级结构的成员类.
4 积累状态.每一个单独的访问者对象都集中了相关的行为,从而也就可以在访
的过程中将执行操作的状态积累在自己的内部,而不是分散到很多节点对象中.
这是有利于系统维护的优点.
缺点:
1 增加新的节点类变得很困难.每增加一个新的节点都意味着要在抽象访问角色
中增加一个新的抽象操作,并在每一个具体访问者类中增加相应的具体操作.
2 破坏封装.访问者模式要求访问者对象访问并调用每一个节点对象的操作,这
隐含了一个对所有节点的要求:它们必须暴露一些自己的操作和内部状态.不然
访问者的访问将变得没有意义.由于访问者对象会自己基类操作所需的转头,从而
这些状态不在存储在节点对象中,这也是破坏了封装.
由于显然的缺点,访问者模式成为一个有争议的设计模式.有些设计师反对使用访问者模式,
而一些设计师则强调访问者模式的优点,还有一些访问者千方百计的设法修改访问者模式,
克服其不足.事实上,尽管有人反对使用这一模式,访问者模式仍然在很多重要系统中使用.
----------------------------------------------------------------------------------------------------------------------
以上就是visitor访问者模式.如有不足请补充
浅谈设计模式-visitor访问者模式的更多相关文章
- 浅谈设计模式--装饰者模式(Decorator Pattern)
挖了设计模式这个坑,得继续填上.继续设计模式之路.这次讨论的模式,是 装饰者模式(Decorator Pattern) 装饰者模式,有时也叫包装者(Wrapper),主要用于静态或动态地为一个特定的对 ...
- 浅谈设计模式--建造器模式(Builder Pattern)
建造器模式,是于创建带有大量参数的对象,并避免因参数数量多而产生的一些问题(如状态不一致-JavaBean的setter模式). 如果参数多且有些是必须初始化的,有些是不一定需要初始化的时候,创建对象 ...
- C++设计模式-Visitor访问者模式
#include <iostream> #include <string> #include <string.h> #include <memory> ...
- 乐在其中设计模式(C#) - 访问者模式(Visitor Pattern)
原文:乐在其中设计模式(C#) - 访问者模式(Visitor Pattern) [索引页][源码下载] 乐在其中设计模式(C#) - 访问者模式(Visitor Pattern) 作者:webabc ...
- 设计模式23:Visitor 访问者模式(行为型模式)
Visitor 访问者模式(行为型模式) 动机(Motivation)在软件构造过程中,由于需求的改变,某些类层次结构中常常需要增加新的行为(方法),如果直接在基类中做这样的修改,将会给子类带来繁重的 ...
- 北风设计模式课程---访问者模式(Visitor)
北风设计模式课程---访问者模式(Visitor) 一.总结 一句话总结: 设计模式是日常问题的经验总结方案,所以学好设计模式对日常出现的问题可以有很好的解决. 访问者设计模式有点神似 抽象工厂模式, ...
- 折腾Java设计模式之访问者模式
博客原文地址:折腾Java设计模式之访问者模式 访问者模式 Represent an operation to be performed on the elements of an object st ...
- C#设计模式总结 C#设计模式(22)——访问者模式(Vistor Pattern) C#设计模式总结 .NET Core launch.json 简介 利用Bootstrap Paginator插件和knockout.js完成分页功能 图片在线裁剪和图片上传总结 循序渐进学.Net Core Web Api开发系列【2】:利用Swagger调试WebApi
C#设计模式总结 一. 设计原则 使用设计模式的根本原因是适应变化,提高代码复用率,使软件更具有可维护性和可扩展性.并且,在进行设计的时候,也需要遵循以下几个原则:单一职责原则.开放封闭原则.里氏代替 ...
- [设计模式] 23 访问者模式 visitor Pattern
在GOF的<设计模式:可复用面向对象软件的基础>一书中对访问者模式是这样说的:表示一个作用于某对象结构中的各元素的操作.它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作.访问 ...
随机推荐
- MySQL UNION 操作符
本教程为大家介绍 MySQL UNION 操作符的语法和实例. 描述 MySQL UNION 操作符用于连接两个以上的 SELECT 语句的结果组合到一个结果集合中.多个 SELECT 语句会删除重复 ...
- MySQL、HBase、ES的对比
hbase是列数据库,是kv结构的,ES的基于Lucene的搜索引擎的面向文档数据库吧 ES是搜索引擎,主要的优势在于快速搜索,HBase是数据库,优势在于存储数据,侧重点不同 MySQL:关系型数据 ...
- Razor Page中的AJAX
1.由于Razor Pages自带提供防伪令牌/验证,用来防止跨站点请求伪造(称为XSRF或CSRF),所以和MVC框架中API使用方式有稍许的不同. 2.所以在我们使用Razor Pages中的fo ...
- 检测代码潜在bug和质量之SonarQube
参数使用 项目分析参数可以在多个地方设置,继承关系如下: 全局分析参数,通过Web UI设置,作用于所有项目(配置–>通用–>通用中设置) 项目分析参数,通过WebUI设置,覆盖全局参数( ...
- PIE SDK矢量栅格化算法
1.算法功能简介 矢量栅格化,由矢量数据向栅格数据的转换一般比较方便.对于点.线目标,由其所在的栅格行.列数表示,对于面状目标,则需判定落人该面积内的像元.通常栅格(像元)尺寸均大于原来坐标表示的分辨 ...
- DevOps 转型到底难不难(转自成哥的世界)
原文:https://mp.weixin.qq.com/s/QwZf6ZsKGNT6YyereSmpQg DevOps 自 2009 年诞生以来,至今整整过去了十年,从最初的摸索,逐步变成一种主流的软 ...
- SpringBoot 整合MyBatis 统一配置bean的别名
所谓别名, 就是在mappper.xml配置文件中像什么resultType="xxx" 不需要写全限定类名, 只需要写类名即可. 配置方式有两种: 1. 在 applicatio ...
- TP5.1 调用common里面自定义的常量
公共文件:\application\common.php define('cms_password', cms); 控制器引用: 调用: $aa = cms_password; dump(cms_pa ...
- WorkFlow一:WorkFlow基础配置
1.使用事物代码SWU3进入WF配置页. 2.展开第一个运行环境维护文件夹,选中第一个配置RFC目标,点击生成.完成后可点击运行按钮测试是否成功. 同上,挨个激活. 3.激活第二个文件夹‘维护环境定义 ...
- MySQL连接超时处理
1.由于MySQL默认是8小时的wait_timeout,当超过8小时的连接时间后,在JAVA中调用将出现如下报错 SEVERE EXCEPTION com.mysql.jdbc.exceptions ...