访问者模式

假设有男人和女人两种元素,要分别打印出他们在不同状态时的不同表现。

用OO的思想把表现(行为)提取出来作为一个抽象方法,代码如下:

用if-else对状态进行判断 
Person接口

public interface Person {
      public void action(String state);
}

Man实现类

Java代码

public class Man implements Person{

public void action(String state) {
if(state == "success"){
System.out.println("当男人成功时,背后多半有一个伟大的女人");
}
else if(state == "love"){
System.out.println("当男人恋爱时,凡事不懂也装懂");
}
}
}

Woman实现类

Java代码

public class Woman implements Person{

public void action(String state) {
if(state == "success"){
System.out.println("当女人成功时,背后大多有一个不成功的男人");
}
else if(state == "love"){
System.out.println("当女人恋爱时,遇事懂也装不懂");
}
}

}

客户端测试代码

Java代码

public class Client {
     public static void main(String[] args) {
Person man = new Man();
Person woman = new Woman();
man.action("success");
woman.action("success");

man.action("love");
woman.action("love");
}
}

结果显示: 
当男人成功时,背后多半有一个伟大的女人 
当女人成功时,背后大多有一个不成功的男人 
当男人恋爱时,凡事不懂也装懂 
当女人恋爱时,遇事懂也装不懂

当需求发生变化时,要增加一种失败状态时,增加男人女人的不同表现,这时就要修改Man类与Woman类的if else,违反了ocp原则(对增加开放-对修改封闭)。而且随着需求的增加,Man类与Woman类的if,else越来越臃肿,需要取消时,又要去修改if,else,既不方便,又容易出错。 
这时候访问者模式可以派上用场了。 
请看下面经修改后的类图:

使用访问者模式

把状态抽象出来成为一个接口(访问者),不同的状态就作为状态的不同实现类(不同的访问者)。 
状态的接口(访问者接口)

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

具体访问者实现类(分别代表不同的状态)

Java代码

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

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

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

public class Love implements Visitor{

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

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

按照类图改造一下人的接口与实现类

Java代码

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

public class Man implements Person{

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

public class Woman implements Person{

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

这时Man与Woman变得轻盈多了,不再需要写一大段的if,else,只需要按不同的状态,传入不同的访问者,执行访问者的方法就OK了。

为了更好地实现客户类与具体元素的解耦,加入一个ObjectStructure类。有了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);
    }
    }
}

客户端测试代码:

Java代码

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);
}
}

这时客户端只依赖于ObjectStructure类与Visitor接口,实现了高度的解耦,具体Visitor实现类的实例化与具体元素(Man,Woman)的创建可以通过工厂模式甚至配合properties/xml配置文件创建,无需客户端关注。而Man与Gril的创建的代码也可以放在其他地方,客户端无需知道,如可以放到ObjectStructure的构造函数中完成

public ObjectStructure(){
attach(new Man());
attach(new Woman());
}

在实例块{ }中完成实例化也可以。这时如果要增加一种状态的不同操作,只需要增加一个具体访问者,无需要修改具体元素类Man与Woman。代码如下,

Java代码

public class Fail implements Visitor{

public void visit(Man man) {
System.out.println("当男人失败时,闷头喝酒,谁也不用劝");
}

public void visit(Woman woman) {
System.out.println("当女人失败时,眼泪汪汪,谁也劝不了");
}
}

修改一下客户端测试代码就OK:

Java代码

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);

//当失败时的不同反映
Visitor fail = new Fail();
o.display(fail);
}
}

结果显示: 
当男人成功时,背后多半有一个伟大的女人 
当女人成功时,背后大多有一个不成功的男人 
当男人恋爱时,凡事不懂也装懂 
当女人恋爱时,遇事懂也装不懂 
当男人失败时,闷头喝酒,谁也不用劝 
当女人失败时,眼泪汪汪,谁也劝不了

现在来让我们看看访问者模式的定义与类图: 
访问者模式定义:表示一个作用于某个对象结构中的各元素的操作。它使可以在不改变各元素的类的前提下定义作用于这些元素的新操作。

访问者模式的特点: 
1)优点:使用了访问者模式以后,对于原来的类层次增加新的操作,仅仅需要实现一个具体访问者角色就可以了,而不必修改整个类层次,使得类层次结构的代码臃肿难以维护。而且这样符合“开闭原则”的要求。而且每个具体的访问者角色都对应于一个相关操作,因此如果一个操作的需求有变,那么仅仅修改一个具体访问者角色,而不用改动整个类层次。

2)访问者模式的双重分派技术 
(1)将具体访问者作为参数传递给具体元素角色 
(2)进入具体元素角色后,具体元素角色调用者作为参数的具体访问者的visit方法,同时将自己(this)作为参数传递进行。具体访问者再根据参数的不同来执行相应的方法

3)前提:开闭原则”的遵循总是片面的。如果系统中的类层次发生了变化,会对访问者模式产生什么样的影响呢?你必须修改访问者接口和每一个具体访问者。因此4人组曾经提出,访问者模式适用于数据结构相对稳定的系统。

4)适用情况:访问者模式的目的是要把处理从数据结构分离出来。很多系统可以按照算法和数据结构分开,如果这样的系统有比较稳定的数据结构,又有易于变化的算法的话,使用访问者模式是比较合适的,因为访问者模式似得算法操作的增加变得容易。反之,如果这样的系统的数据结构对象易于变化,经常要有新的数据对象增加进来,就不适合使用访问者模式。

 
(0)

相关推荐

  • 设计模式(十六)——访问者模式

    设计模式(十六)——访问者模式

  • 诚之和:设计模式之什么是访问者模式

    本篇内容介绍了"设计模式之什么是访问者模式"的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学 ...

  • Dataset之CelebA&man2woman:CelebA&man2woman 数据集的简介、安装、使用方法之详细攻略

    Dataset之CelebA&man2woman:CelebA&man2woman 数据集的简介.安装.使用方法之详细攻略 CelebA&man2woman 数据集的简介 1. ...

  • 软件设计模式修炼 -- 访问者模式

    访问者模式是一种较为复杂的行为型设计模式,它包含访问者和被访问元素两个主要组成部分,这些被访问的元素具有不同的类型,且不同的访问者可以对其进行不同的访问操作 模式动机 对于系统中某些对象,它们存储在同 ...

  • C#设计模式学习笔记:(21)访问者模式

    本笔记摘抄自:https://www.cnblogs.com/PatrickLiu/p/8135083.html,记录一下学习过程以备后续查用. 一.引言 今天我们要讲行为型设计模式的第九个模式--访 ...

  • PHP设计模式—访问者模式

    定义: 访问者模式(Visitor):表示一个作用于某对象结构中的各元素的操作.它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作. 结构: Visitor:抽象访问者,为该对象结构中Co ...

  • PHP设计模式之访问者模式

    PHP设计模式之访问者模式 访问者,就像我们去别人家访问,或者别人来我们家看望我们一样.我们每个人都像是一个实体,而来访的人都会一一的和我们打招呼.毕竟,我们中华民族是非常讲究礼数和好客的民族.访问者 ...

  • [PHP小课堂]PHP设计模式之访问者模式

    [PHP小课堂]PHP设计模式之访问者模式 关注公众号:[硬核项目经理]获取最新文章 添加微信/QQ好友:[DarkMatterZyCoder/149844827]免费得PHP.项目管理学习资料

  • 设计模式-行为型-访问者模式

    访问者模式(Vistor): 访问者模式的官方定义是这样的:表示一个作用于某对象结构中的各元素的操作,它使你可以在不改变各元素类的前提下定义作用于这些元素的新操作.官方的东西总是晦涩难懂的,那么我们现 ...

  • 简说设计模式——访问者模式

    一.什么是访问者模式 访问者模式是一个相对比较简单,但结构又稍显复杂的模式,它讲的是表示一个作用于某对象结构中的各元素的操作,它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作.例如,你在 ...