【设计模式】访问者模式

系列文章目录


文章目录


男人和女人!

网上对男女问题讨论度一直都不低,有的人说:"男人结婚时判了有'妻'徒刑,女人结了婚就是买了爱情保险"。事实也确实是这样,而访问者模式讲的就是表示一个作用于某对象结构中的各元素的操作。 它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作。其实我们也可以将这个两个东西混在一起来学习。我们实现把这些男女对比的例子打在屏幕上。

最简单的编程实现

java 复制代码
	    System.out.println("男人成功时,背后多半有一个伟大的女人。");
        System.out.println("女人成功时,背后大多有一个不成功的男人");
        System.out.println("男人失败时,闷头喝酒,谁也不用劝");
        System.out.println("女人失败时,眼泪汪汪,谁也劝不了");
        System.out.println("男人恋爱时,凡事不懂也要装懂");
        System.out.println("女人恋爱时,遇事懂也装作不懂");

这样的代码也就太蠢了,我们可以通过提炼类,共享方法来优化代码。

简单的面向对象实现代码

"人"类,是男人和女人类的抽象类。

java 复制代码
abstract class Person{
        protected String action;
        public String getAction(){
            return this.action;
        }
        public void set(String value){
            this.action = value;
        }
        //得到结论或者反应
        public abstract void getConclusion();
    }

男人 类:

java 复制代码
 class Man extends Person{
        //得到结论或反应
        @Override
        public void getConclusion() {
            if(action == "成功"){
                System.out.println(this.getClass().getSimpleName() + this.action + "时,背后多半有个成功的女人。");
            }else if(action == "失败"){
                System.out.println(this.getClass().getSimpleName() + this.action + "时,闷头喝酒,谁也不用劝");
            }else if(action == "恋爱"){
                System.out.println(this.getClass().getSimpleName() + this.action +"时,凡事不懂也要装懂。");
            }
        }
    }

女人类:

java 复制代码
class Woman extends Person{
        //得到结论或反应
        @Override
        public void getConclusion() {
            if(action == "成功"){
                System.out.println(this.getClass().getSimpleName() + this.action + "时,背后多半有个不成功的男人。");
            }else if(action == "失败"){
                System.out.println(this.getClass().getSimpleName() + this.action + "时,眼泪汪汪,谁也劝不了");
            }else if(action == "恋爱"){
                System.out.println(this.getClass().getSimpleName() + this.action +"时,凡事懂也装作不懂。");
            }
        }
    }

客户端代码:

java 复制代码
public static void main(String[] args) {
        ArrayList<Person> persons = new ArrayList<Person>();
        Person man1 = new Man();
        man1.setAction("成功");
        persons.add(man1);
        Person woman1 = new Woman();
        woman1.setAction("成功");
        persons.add(woman1);
        Person man2 = new Man();
        man2.setAction("失败");
        persons.add(man2);
        Person woman2 = new Woman();
        woman2.setAction("失败");
        persons.add(woman2);
        Person man3 = new Man();
        man3.setAction("恋爱");
        persons.add(man3);
        Person Woman3 = new Woman();
        Woman3.setAction("恋爱");
        persons.add(Woman3);
        for(Person item : persons){
            item.getConclusion();
        }
    }

结果显示:

Man成功时,背后多半有一个伟大的女人

Woman成功时,背后大多有一个不成功的男人,

Man失败时,闷头喝酒,谁也不用劝。

Woman失败时,眼泪汪汪,谁也劝不了

man恋爱时,凡事不懂也要装懂。

woman恋爱时,遇事懂也装作不懂

现在算得上是面向对象编程了,但是那些if else似乎很碍眼,如果要是加一个结婚的状态,那就又要修改代码,但是呢这些状态又无法写成类,这里其实就可以用到访问者模式。

用了模式的实现

状态的抽象类和人的抽象类:

java 复制代码
 abstract class Action{
        //得到男人结论或反应
        public abstract void getManConclusion(Man concreteElementA);
        //得到女人结论或反应
        public abstract void getWomanConclusion(Woman concreteElement);
    }
    //人类抽象类
    abstract class Person{
        //接受
        public abstract void accept(Action visitor);
    }

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

具体状态类:

java 复制代码
class Success extends Action{

        @Override
        public void getManConclusion(Man concreteElementA) {
            System.out.println(concreteElementA.getClass().getSimpleName() + " " 
                    + this.getClass().getSimpleName() + "时,背后多半有一个伟大的女人");
        }

        @Override
        public void getWomanConclusion(Woman concreteElementB) {
            System.out.println(concreteElementB.getClass().getSimpleName() + " " + 
                    this.getClass().getSimpleName() + "时,背后多半有一个伟大的女人");
        }
    }

失败和恋爱状态代码与上类似。

男人类和女人类:

java 复制代码
class Man extends Person{

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

        @Override
        public void accept(Action visitor) {
            visitor.getWomanConclusion(this);
        }
    }

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

由于总是需要'男人'与'女人'在不同状态的对比,所以我们需要一个'对象结构'类来针对不同的'状态'遍历'男人'与'女人',得到不同的反应。

java 复制代码
 class ObjectStructure{
        private ArrayList<Person> elements = new ArrayList<>();
        //增加
        public void attach(Person element){
            elements.add(element);
        }
        //移除
        public void detach(Person element){
            elements.remove(element);
        }
        //查看显示
        public void display(Action visitor){
            for(Person e : elements){
                e.accept(visitor);
            }
        }
    }

客户端代码:

java 复制代码
    public static void main(String[] args) {
        ObjectStructure o = new ObjectStructure();
        o.attach(new Man());
        o.attach(new Woman());
        
        //成功时的反应
        Success v1 = new Success();
        o.display(v1);
        
        //失败时的反应
        Failing v2 = new Failing();
        o.display(v2);
        
        //恋爱时的反应
        Amativeness v3  =new Amativeness();
        o.display(v3);
    }

这样做,就意味着,如果我们现在要增加结婚的状态来考察男人和女人的反应,只需要增加一个状态的子类就可以了,因为我们用了双分派。

结婚状态类:

java 复制代码
 class Marriage extends Action{

        @Override
        public void getManConclusion(Man concreteElementA) {
            System.out.println(concreteElementA.getClass().getSimpleName() + " " + this.getClass().getSimpleName()
            +" 时,感慨到:有妻徒刑遥遥无期");
        }

        @Override
        public void getWomanConclusion(Woman concreteElementB) {
            System.out.println(concreteElementB.getClass().getSimpleName()
            + " " + this.getClass().getSimpleName() + "时,欣慰说:婚姻保险保平安");
        }
    }

客户端代码增加下面一段代码就可以实现:

java 复制代码
Maeeiage v4 = new Marriage();
o.dislay(v4);

经过这样的修改,完美体现了开放-封闭原则,这就叫做访问者模式

访问者模式

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

之所以男女这个例子可以用访问者模式,,是因为人类在性别上只有男人和女人两类。如果人类的性别不止是男女,而是可有多种性别,那就意味这,状态类中的抽象方法就不可能稳定了,每加一种类别,就需要在状态类和它的所有下属类中都增加一个方法。这就不符合开放-封闭原则。也就是说,访问者模式适用于数据结构相对稳定的系统它把数据结构和作用于结构上的操作之间的耦合解脱开,使得操作集合可以相对自由的演化。

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

所以,访问者模式的优点就是增加新的操作很容易,因为增加新的操作就意味着增加一个新的访问者增加一个新的访问者。访问者模式将有关的行为集中到一个访问者对象中。
缺点也就是使增加新的数据结构变得困难了。

下面看,访问者模式基本代码:

访问者模式基本代码:

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

java 复制代码
public abstract class Visitor {
    public abstract void visitConcreteElementA(ConcreteElementA concreteElementA);
    public abstract void visitConcreteElementB(ConcreteElementB concreteElementB);
}

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

java 复制代码
public class ConcreteVisitor1 extends Visitor{
    @Override
    public void visitConcreteElementA(ConcreteElementA concreteElementA) {
        System.out.println("A被1访问");
    }
    @Override
    public void visitConcreteElementB(ConcreteElementB concreteElementB) {
        System.out.println("B被1访问");
    }
}

Element类:定义一个Accept的操作,它以一个访问者为参数

java 复制代码
public abstract class Element {
    public abstract void accept(Visitor visitor);
}

ConcreteElementA和ConcreteElementB类,具体元素,实现Accept操作:

java 复制代码
public class ConcreteElementA extends Element{
    @Override
    public void accept(Visitor visitor) {
        visitor.visitConcreteElementA(this);
    }
}
public class ConcreteElementB extends Element{
    @Override
    public void accept(Visitor visitor) {
        visitor.visitConcreteElementB(this);
    }
}

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

java 复制代码
public class ObjectStructure {
    private ArrayList<Element> elements = new ArrayList<>();
    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);
        }
    }
}

客户端代码:

java 复制代码
public static void main(String[] args) {
        ObjectStructure o = new ObjectStructure();
        o.attach(new ConcreteElementA());
        o.attach(new ConcreteElementB());

        ConcreteVisitor1 v1 = new ConcreteVisitor1();
        ConcreteVisitor2 v2 = new ConcreteVisitor2();

        o.accept(v1);
        o.accept(v2);
    }
}

总结

以上就是本文全部内容,先是通过男女对待问题不同的反应引出访问者模式,之后简单将实例代码化,之后将其改造为访问者模式代码,最后引出访问者模式模板代码,并分析访问者模式的用处以及优缺点。感谢各位能够看到最后,如有问题,欢迎各位大佬在评论区指正,希望大家可以有所收获!创作不易,希望大家多多支持!

相关推荐
Mr.45673 小时前
idea常用设置和常用插件---持续更新(自己留存备份)
java·ide·intellij-idea
老华带你飞3 小时前
寝室快修|基于SprinBoot+vue的贵工程寝室快修小程序(源码+数据库+文档)
java·数据库·vue.js·spring boot·小程序·毕设·贵工程寝室快修
杨杨杨大侠3 小时前
手把手教你写 httpclient 框架(八)- 实战案例集锦
java·http·github
肖老师xy3 小时前
uniapp 苹果端bug合集
java·服务器·uni-app
无名客03 小时前
常用设计模式中的工厂模式,责任链模式,策略模式和单例模式的简单说明
设计模式·责任链模式·策略模式
zlpzlpzyd3 小时前
idea 2025.2 在Windows 11中执行maven命令中文乱码处理
java·maven·intellij-idea
刘一说3 小时前
IntelliJ IDEA 查找和替换使用指南
java·ide·intellij-idea
白露与泡影5 小时前
2025年高质量Java面试真题汇总
java·python·面试
꒰ঌ 安卓开发໒꒱5 小时前
Java 面试 -Java基础
java·开发语言·面试