大话设计模式——访问者模式

男女对比——最简单的实现

访问者模式讲的是表示一个作用于某对象结构中的各元素的操作。它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作。

public class Client {
    // 最简单的编程实现
    public static void main(String[] args) {
        System.out.println("男人成功时,背后多半有一个伟大的女人。");
        System.out.println("女人成功时,背后大多有一个不成功的男人。");
        System.out.println("男人失败时,闷头喝酒,谁也不用劝。");
        System.out.println("女人失败时,眼泪汪汪,谁也劝不了。");
        System.out.println("男人恋爱时,凡事不懂也要装懂。");
        System.out.println("女人恋爱时,遇事懂也装作不懂。");
    }
}

男女对比——面向对象编程

人类

public abstract class Person {
    protected String action;

    // 得到结论或反应
    public abstract void getConclusion();

    public String getAction() {
        return action;
    }

    public void setAction(String action) {
        this.action = action;
    }

}

男人类

public class Man extends Person {

    @Override
    public void getConclusion() {
        if (action.equals("成功")) {
            System.out.println(String.format("%s%s时,背后多半有一个伟大的女人。", this.getClass().getSimpleName(), action));
        } else if (action.equals("失败")) {
            System.out.println(String.format("%s%s时,闷头喝酒,谁也不用劝。", this.getClass().getSimpleName(), action));
        } else if (action.equals("恋爱")) {
            System.out.println(String.format("%s%s时,凡事不懂也要装懂。", this.getClass().getSimpleName(), action));
        }
    }

}

女人类

public class Woman extends Person {

    @Override
    public void getConclusion() {
        if (action.equals("成功")) {
            System.out.println(String.format("%s%s时,背后大多有一个不成功的男人。", this.getClass().getSimpleName(), action));
        } else if (action.equals("失败")) {
            System.out.println(String.format("%s%s时,眼泪汪汪,谁也劝不了。", this.getClass().getSimpleName(), action));
        } else if (action.equals("恋爱")) {
            System.out.println(String.format("%s%s时,遇事懂也装作不懂。", this.getClass().getSimpleName(), action));
        }
    }

}

客户端

/**
 * 男人类和女人类中的if...else问题,如果再增加一个“结婚”状态呢?
 */
public class Client {
    public static void main(String[] args) {
        List<Person> persons = new ArrayList<Person>();
        Man man1 = new Man();
        man1.setAction("成功");
        persons.add(man1);
        Woman woman1 = new Woman();
        woman1.setAction("成功");
        persons.add(woman1);
        
        Man man2 = new Man();
        man2.setAction("失败");
        persons.add(man2);
        Woman woman2 = new Woman();
        woman2.setAction("失败");
        persons.add(woman2);
        
        Man man3 = new Man();
        man3.setAction("恋爱");
        persons.add(man3);
        Woman woman3 = new Woman();
        woman3.setAction("恋爱");
        persons.add(woman3);
        for (Person person : persons) {
            person.getConclusion();
        }
    }
}

在这里插入图片描述

男女对比——访问者模式

在这里插入图片描述

状态的抽象类

这里的关键就在于人只分为男人和女人,这个性别的分类是稳定的,所以可以在状态类中,增加“男人反应”和“女人反应”两个方法,方法个数是稳定的,不会很容易的发生变化。而“人”抽象类中有一个抽象方法“接受”,它是用来获得“状态”对象的。

每一种具体状态都继承“状态”抽象类,实现两个反应的方法。


public abstract class Action {
    // 得到男人结论或反应
    public abstract void getManConclusion(Man man);
    // 得到女人结论或反应
    public abstract void getWomanConclusion(Woman woman);
}

人的抽象类

public abstract class Person {
    // 接受,它是用来获得“状态”对象的
    public abstract void accept(Action visitor);
}

具体“状态”类

public class Success extends Action {
    @Override
    public void getManConclusion(Man man) {
        System.out.println(String.format("%s %s时,背后多半有一个伟大的女人。", man.getClass().getSimpleName(), this.getClass().getSimpleName()));
    }
    @Override
    public void getWomanConclusion(Woman woman) {
        System.out.println(String.format("%s %s时,背后大多有一个不成功的男人。", woman.getClass().getSimpleName(), this.getClass().getSimpleName()));
    }
}
// 失败
public class Failing extends Action{

    @Override
    public void getManConclusion(Man man) {
        System.out.println(String.format("%s %s时,闷头喝酒,谁也不用劝。", man.getClass().getSimpleName(), this.getClass().getSimpleName()));
    }

    @Override
    public void getWomanConclusion(Woman woman) {
        System.out.println(String.format("%s %s时,眼泪汪汪,谁也劝不了。", woman.getClass().getSimpleName(), this.getClass().getSimpleName()));
    }

}
// 恋爱
public class Amativeness extends Action{

    @Override
    public void getManConclusion(Man man) {
        System.out.println(String.format("%s %s时,凡事不懂也要装懂。", man.getClass().getSimpleName(), this.getClass().getSimpleName()));
    }

    @Override
    public void getWomanConclusion(Woman woman) {
        System.out.println(String.format("%s %s时,遇事懂也装作不懂。", woman.getClass().getSimpleName(), this.getClass().getSimpleName()));
    }

}

男人类和女人类

首先在客户程序中将具体状态作为参数传递给“男人”类完成了一次分派,然后“男人”类调用作为参数的“具体状态”中的方法“男人反应”,同时将自己(this)作为参数传递进去。这便是完成了第二次分派。双分派意味着得到执行的操作决定于请求的种类和两个接收者的类型。“接受”方法就是一个双分派的操作,它得到执行的操作不仅决定于“状态”类的具体状态,还决定于它访问的“人”的类别。

public class Man extends Person {

    // 双分派技术
    @Override
    public void accept(Action visitor) {
        visitor.getManConclusion(this);
    }

}
public class Woman extends Person {
    @Override
    public void accept(Action visitor) {
        visitor.getWomanConclusion(this);
    }
}

对象结构

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

    // 查看显示
    public void display(Action visitor) {
        for (Person person : elements) {
            person.accept(visitor);
        }
    }
}

客户端

public class Client {
    public static void main(String[] args) {
        // 在对象结构中要加入要对比的“男人”和“女人”
        ObjectStructure objectStructure = new ObjectStructure();
        objectStructure.attach(new Man());
        objectStructure.attach(new Woman());

        // 成功时的反应
        Success success = new Success();
        // 查看在各种状态下,“男人”和“女人”的反应
        objectStructure.display(success);

        // 失败时的反应
        Failing failing = new Failing();
        objectStructure.display(failing);

        // 恋爱时的反应
        Amativeness amativeness = new Amativeness();
        objectStructure.display(amativeness);

        // 结婚时的反应
        Marriage marriage = new Marriage();
        objectStructure.display(marriage);
    }
}

在这里插入图片描述

访问者模式

访问者模式:表示一个作用于某对象结构中的各元素的操作。它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作。
应该是GOF中最复杂的一个模式了。

Element就是“人”类,ConcreteElementA和ConcreteElementB就是“男人”和“女人”.
Visitor就是“状态”类,具体的ConcreteVisitor就是“成功”、“失败”、“恋爱”等状态。
ObjectStructure就是“对象结构”类了。

男女对比这么多的原因是因为人类在性别上就只有男人和女人两类。而这也正是访问者模式可以实施的前提。因为如果人类的性别不只是男女,那么“状态”类中的抽象方法就不可能稳定了,每加一种类别,就需要在状态类和它的所有下属类中都增加一个方法,这就不符合开闭原则。

访问者模式适用于数据结构相对稳定的系统。它把数据结构和作用于结构上的操作之间的耦合解脱开,使得操作集合可以相对自由地演化。

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

访问者模式的优点就是增加新的操作很容易,因为增加新的操作就意味着增加一个新的访问者。访问者模式将有关的行为集中到一个访问者对象中。

通常ConcreteVisitor可以单独开发,不必跟ConcreteElementA或ConcreteElementB写在一起。正因为这样,ConcreteVisitor能提高ConcreteElement之间的独立性。如果把一个处理动作设计成ConcreteElementA和ConcreteElementB类的方法,每次想新增“处理”以扩充功能时就得去修改ConcreteElementA和ConcreteElementB了。

访问者模式的缺点其实也就是使增加新的数据结构变得困难了。

GOF中的一个作者说过:大多时候你并不需要访问者模式,但当一旦你需要访问者模式时,那就是真的需要它了。

在这里插入图片描述

Visitor类

Visitor类,为该对象结构中ConcreteElement的每一个类声明一个Visit操作。

public abstract class Visitor {
    public abstract void visitConcreteElementA(ConcreteElementA concreteElementA);
    public abstract void visitConcreteElementB(ConcreteElementB concreteElementB);
}

ConcreteVisitor

ConcreteVisitor1和ConcreteVisitor2类,具体访问者,实现每个由Visitor声明的操作。
每个操作实现算法的一部分,而该算法片段乃是对应于结构中对象的类。

public class ConcreteVisitor1 extends Visitor {

    @Override
    public void visitConcreteElementA(ConcreteElementA concreteElementA) {
        System.out.println(String.format("%s被%s访问",concreteElementA.getClass().getSimpleName(),this.getClass().getSimpleName()));
    }

    @Override
    public void visitConcreteElementB(ConcreteElementB concreteElementB) {
        System.out.println(String.format("%s被%s访问",concreteElementB.getClass().getSimpleName(),this.getClass().getSimpleName()));
    }

}
public class ConcreteVisitor2 extends Visitor {

    @Override
    public void visitConcreteElementA(ConcreteElementA concreteElementA) {
        System.out.println(String.format("%s被%s访问",concreteElementA.getClass().getSimpleName(),this.getClass().getSimpleName()));
    }

    @Override
    public void visitConcreteElementB(ConcreteElementB concreteElementB) {
        System.out.println(String.format("%s被%s访问",concreteElementB.getClass().getSimpleName(),this.getClass().getSimpleName()));
    }

}

Element类

Element类,定义一个accept操作,它以一个访问者为参数。

public abstract class Element {
    public abstract void accept(Visitor visitor);
}

ConcreteElement

具体元素,实现Accept操作。

public class ConcreteElementA extends Element {
    @Override
    public void accept(Visitor visitor) {
        // 充分利用双分派技术,实现处理与数据结构的分离
        visitor.visitConcreteElementA(this);
    }
    // 其他的相关方法
    public void operationA() {

    }

}
public class ConcreteElementB extends Element {
    @Override
    public void accept(Visitor visitor) {
        // 充分利用双分派技术,实现处理与数据结构的分离
        visitor.visitConcreteElementB(this);
    }

    // 其他的相关方法
    public void operationB() {

    }
}

ObjectStructure类

ObjectStructure类,能枚举它的元素,可以提供一个高层的接口以允许访问者访问它的元素。

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

    public void attach(Element element) {
        elements.add(element);
    }

    public void detach(Element element) {
        elements.remove(element);
    }

    public void accept(Visitor visitor) {
        for (Element element : elements) {
            element.accept(visitor);
        }
    }
}

客户端

public class Client {
    public static void main(String[] args) {
        ObjectStructure objectStructure = new ObjectStructure();
        objectStructure.attach(new ConcreteElementA());
        objectStructure.attach(new ConcreteElementB());

        ConcreteVisitor1 concreteVisitor1 = new ConcreteVisitor1();
        ConcreteVisitor2 concreteVisitor2 = new ConcreteVisitor2();

        objectStructure.accept(concreteVisitor1);
        objectStructure.accept(concreteVisitor2);
    }
}

在这里插入图片描述

注:本文内容源自程杰的《大话设计模式》

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值