访问者模式

面试:你懂什么是分布式系统吗?Redis分布式锁都不会?>>>   hot3.png

一、引子

  对于系统中一个已经完成的类层次结构,我们已经给它提供了满足需求的接口。但是面对新增加的需求,我们应该怎么做呢?如果这是为数不多的几次变动,而且你不用为了一个需求的调整而将整个类层次结构统统地修改一遍,那么直接在原有类层次结构上修改也许是个不错的主意。

  但是往往我们遇到的却是:这样的需求变动也许会不停的发生;更重要的是需求的任何变动可能都要让你将整个类层次结构修改个底朝天……。这种类似的操作分布在不同的类里面,不是一个好现象,我们要对这个结构重构一下了。

  那么,访问者模式也许是你很好的选择。

   二、定义与结构

  访问者模式,顾名思义使用了这个模式后就可以在不修改已有程序结构的前提下,通过添加额外的“访问者”来完成对已有代码功能的提升。

 《设计模式》一书对于访问者模式给出的定义为:表示一个作用于某对象结构中的各元素的操作。它使你可以在不改变各元素的类的前提下定义作用于这些元素的 新操作。从定义可以看出结构对象是使用访问者模式必须条件,而且这个结构对象必须存在遍历自身各个对象的方法。这便类似于java中的 collection概念了。

  以下是访问者模式的组成结构:

  1) 访问者角色(Visitor):为该对象结构中具体元素角色声明一个访问操作接口。该操作接口的名字和参数标识了发送访问请求给具体访问者的具体元素角色。这样访问者就可以通过该元素角色的特定接口直接访问它。

  2) 具体访问者角色(Concrete Visitor):实现每个由访问者角色(Visitor)声明的操作。

  3) 元素角色(Element):定义一个Accept操作,它以一个访问者为参数。

  4) 具体元素角色(Concrete Element):实现由元素角色提供的Accept操作。

  5) 对象结构角色(Object Structure):这是使用访问者模式必备的角色。它要具备以下特征:能枚举它的元素;可以提供一个高层的接口以允许该访问者访问它的元素;可以是一个复合(组合模式)或是一个集合,如一个列表或一个无序集合。

  来张类图就能更加清晰的看清访问者模式的结构了。

 

那么像引言中假想的。我们应该做些什么才能让访问者模式跑起来呢?首先我们要在原有的类层次结构中添加accept方法。然后将这个类层次中的类放到一个对象结构中去。这样再去创建访问者角色……

  三、举例

 


public interface Person {
      void accept(Visitor visitor);
}


public class Woman implements Person{

	public void accept(Visitor visitor) {
          visitor.visit(this);
	}
}


public class Man implements Person{

	public void accept(Visitor visitor) {
		visitor.visit(this);
	}
}


访问者接口与实现类,分别代表男人与女人在不同的状态下的表现


public interface Visitor {
      public void visit(Man man);
      public void visit(Woman girl);
}

//成功时Man与Woman的不同表现
public class Success implements Visitor{

	public void visit(Man man) {
		System.out.println("当男人成功时,背后多半有一个伟大的女人");
	}


	public void visit(Woman woman) {
		System.out.println("当女人成功时,背后大多有一个不成功的男人");
	}
}

//恋爱时Man与Woman的不同表现
public class Love implements Visitor{

	public void visit(Man man) {
		System.out.println("当男人恋爱时,凡事不懂也装懂");
	}


	public void visit(Woman girl) {
		System.out.println("当女人恋爱时,遇事懂也装不懂");
	}
}

ObjectStructure与客户端测试代码


import java.util.*;

public class ObjectStructure {
    private List<Person> elements = new ArrayList<Person>();

    public void attach(Person element){
    	elements.add(element);
    }
    
    public void detach(Person element){
    	elements.remove(elements);
    }
    
    //遍历各种具体元素并执行他们的accept方法
    public void display(Visitor visitor){
    	for(Person p:elements){
    		p.accept(visitor);
    	}
    }
}


public class Client {
      public static void main(String[] args) {
		ObjectStructure o = new ObjectStructure();  //依赖于ObjectStructure
		//实例化具体元素
		o.attach(new Man());  
		o.attach(new Woman());
		
		//当成功时不同元素的不同反映
		Visitor success = new Success();           //依赖于抽象的Visitor接口
		o.display(success);
		
		//当恋爱时的不同反映
		Visitor amativeness = new Love();          //依赖于抽象的Visitor接口
		o.display(amativeness);
		
	}
}

既定访问者模式的类图:

 

 

 

 

 

 

需求的变化

假设现在需求要扩展数据结构,增加一种具体元素,男与女之外的一种不明物体,我们暂时把它称为“怪兽”,在既有访问者模式的架构下,应该怎样?首先增加一个Bruce类,实现Person接口。最麻烦的是要修改访问者接口及其所有具体访问者!

因为Visit方法中没有包含访问Bruce对象的行为,因此我们被迫要去手工更改Visitor(包括抽象的,具体的),在其中添加有关Bruce对象 的行为,这严重违反了“开放-封闭”原则。究其原因在于目前的结构下,被访问对象与访问对象互相依赖,自然不利于分离变化,必须去掉一层依赖关系。

我们尝试把Visitor对Person(元素)的依赖关系去掉,抽象出对应每个具体元素的ElementVisitor接口 -->ManVisitor,WomanVisitor,然后把Visitor对Person的依赖关系转移到ManVisitor与 WomanVisitor身上。
改造后访问者模式的类图:

现在Visitor接口已经没有任何抽象方法,只是一个空接口,每一个具体元素对应有一个ElementVisitor接口,每一个元素对应的ElementVisitor接口有访问该元素的visit(),相当把原来在Visitor接口中声明工作,交由各个具体ElementVisitor接口完成。

经过改造后的代码:

public interface Visitor {
      //退化到没有任何抽象方法
}

新增加ManVisitor,WomanVisitor接口

public interface ManVisitor {
      public void visit(Man man);
}

public interface WomanVisitor {
      public void visit(Woman w);
}








具体Visitor实现类现在同时实现3个接口

//由实现Visitor接口扩展成实现Visitor,WomanVisitor,ManVisitor三个接口
public class Success implements Visitor,WomanVisitor,ManVisitor{

    public void visit(Man man) {
        System.out.println("当男人成功时,背后多半有一个伟大的女人");
    }

    public void visit(Woman girl) {
        System.out.println("当女人成功时,背后大多有一个不成功的男人");
    }
}


//由实现Visitor接口扩展成实现Visitor,WomanVisitor,ManVisitor三个接口
public class Love implements Visitor,WomanVisitor,ManVisitor{

    public void visit(Man man) {
        System.out.println("当男人恋爱时,凡事不懂也装懂");
    }

    public void visit(Woman girl) {
        System.out.println("当女人恋爱时,遇事懂也装不懂");
    }
}

Person接口没有变化,依旧只依赖于Visitor接口

  1. public interface Person {  
  2.       void accept(Visitor visitor);  
  3. }

改造后的具体元素类Man与Woman

public class Man implements Person {

    // 先对visitor进行类型转换,再执行visit方法,因为Visitor接口已经没有声明任何抽象方法了
    public void accept(Visitor visitor) {
        if (visitor instanceof ManVisitor) {
            ManVisitor mv = (ManVisitor) visitor;
            mv.visit(this);
        }
    }
}


public class Woman implements Person {

    // 先对visitor进行类型转换,再执行visit方法,因为Visitor接口已经没有声明任何抽象方法了
    public void accept(Visitor visitor) {
        if (visitor instanceof WomanVisitor) {
            WomanVisitor wv = (WomanVisitor) visitor;
            wv.visit(this);
        }
    }
}

ObjectStructure与客户端测试代码没有变化

import java.util.*;

public class ObjectStructure {
    private List<Person> elements = new ArrayList<Person>();

    public void attach(Person element){
        elements.add(element);
    }
    
    public void detach(Person element){
        elements.remove(elements);
    }
    
    //遍历各种具体元素并执行他们的accept方法
    public void display(Visitor visitor){
        for(Person p:elements){
            p.accept(visitor);
        }
    }
}


public class Client {
      public static void main(String[] args) {
        ObjectStructure o = new ObjectStructure();  //依赖于ObjectStructure
        //实例化具体元素
        o.attach(new Man());  
        o.attach(new Woman());
        
        //当成功时不同元素的不同反映
        Visitor success = new Success();           //依赖于抽象的Visitor接口
        o.display(success);
        
        //当恋爱时的不同反映
        Visitor amativeness = new Love();          //依赖于抽象的Visitor接口
        o.display(amativeness);        
    }
}



 

 

 

 

 

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
访问者模式是GOF设计模式中的一种行为型模式。它被描述为最复杂且最难以理解的一种模式。访问者模式的目的是封装一些作用于某种数据结构中的各元素的操作,可以在不改变这些元素的类的前提下定义这些操作。它通过将数据结构与操作分离,使得可以在不改变数据结构的前提下添加新的操作,提高了代码的灵活性和可扩展性。 访问者模式的构成包括元素(Element)、访问者(Visitor)和对象结构(Object Structure)。元素表示数据结构中的各个元素,它们通常会提供一个接受访问者的方法。访问者则表示对元素的操作,它们可以根据具体的元素类型进行不同的操作。对象结构则是一个容器,用于存放元素,并提供让访问者访问元素的接口。 访问者模式适用于数据结构相对稳定,但其操作经常发生变化的情况。它可以将操作的变化封装在访问者中,而无需改变元素的类。这样一来,当需要新增一种操作时,只需要新增一个访问者,而不需要改变元素的类。 访问者模式的优点包括增加新的操作非常方便,由于具体访问者类与具体元素类之间没有直接的关联,因此增加新的访问者类对原有类库无影响。同时,它也符合开闭原则,对于元素的类库可以在不修改源代码的情况下添加新的操作。 总结起来,访问者模式是一种通过将数据结构与操作分离的设计模式,可以在不改变数据结构的前提下添加新的操作。它的构成包括元素、访问者和对象结构,适用于数据结构相对稳定,但操作经常变化的情况。访问者模式的优点包括增加新的操作方便,符合开闭原则。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* [详解设计模式:访问者模式](https://blog.csdn.net/weixin_45187434/article/details/128197861)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *3* [设计模式 访问者模式](https://download.csdn.net/download/zjn640322/9685149)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值