访问者模式

在诸多设计模式中,个人一直很难理解解释器模式和访问者模式【可能用相对少吧】。这两天决定去再次认真看看访问者模式,在网上搜了搜,发现给出的答案往往太过于“为了演示模式而举例“,看了之后还是一头雾水【知道了如何用,却很难理解为什么要用- -】,最终在网上找到一篇个人觉得举例还算贴近生活【具体作者忘了,在此说声谢谢】,看完后,加上自己的一番思考,方有了本篇博文。

还是以网上那篇博文进行举例【但是又做了些许修改和个人思考过程,觉得这样方便理解】

场景: 交通工具在不同时期价格的浮动。

首先看看没有使用访问者模式的情况:
有火车(Train)和汽车(Bus)两种交通工具(Vehicle)【你可以想象有很多交通工具,飞机,动车。。。】,现在要求在过年期间价格上调。代码如下:

//Vehicle.java
package com.xiaolu.normal;

public interface Vehicle {
    public void showPrice();

    public void priceChange();
}

//Bus.java
package com.xiaolu.normal;

public class Bus implements Vehicle {

    @Override
    public void showPrice() {
        System.out.println("汽车当前票价为80元");
    }

    @Override
    public void priceChange() {
        System.out.println("过年了,价格上涨5%");
    }

}

//Train.java
package com.xiaolu.normal;

public class Train implements Vehicle {

    @Override
    public void showPrice() {
        System.out.println("火车当前票价为 50元");
    }

    @Override
    public void priceChange() {
        System.out.println("过年高峰期,在原有基础上价格上调10%");
    }

}

//Client.java    --客户端
package com.xiaolu.normal;

public class Client{
    public static void main(String[] args) {
        Vehicle v = new Bus();
        v.showPrice();
        v.priceChange();

        System.out.println("--------------------------------------------------");

        v = new Train();
        v.showPrice();
        v.priceChange();
    }
}

输出:
汽车当前票价为80元
过年了,价格上涨5%
--------------------------------------------------
火车当前票价为 50元
过年高峰期,在原有基础上价格上调10%

现在需求有变化,要求在国庆期间价格在原有基础上【平时的价格】上调 一些,怎么办呢?我们如何应对这个需求?
修改Vehicle接口?那么势必它的所有实现者都必须跟着变【实现者可能非常多额】,好,你忍受了这次痛苦,那么下次需求说中秋节价格也要变化呢?是否又得修改接口呢? 这还只是一个维度的变化,如果需求要求车辆可以显示出自己的最高运行速度呢???接口又要变化吗?
我记得某本软件工程类书中说过”不要被同一颗子弹打中两次“【或者说”戏弄我一次,是你蠢;戏弄我两次,是我蠢 - - 】,那么如果你一直改接口,随着需求的变化你不是被“打中两次”,而是直接“被打死”啊- -
那么接下来,本着访问者模式的实现思想【这里我是选择了逆推,因为没有看过本模式的人,很难自己去思考出来这种方式,我们逆着推,一步步进行,只要最终理解即可】

在需求第一次改变的时候【此时要求国庆也有价格调整】,我们就做出对应的对策:

//Vehicle.java
package com.xiaolu.visitpattern;

public interface Vehicle {
    public void showPrice();

    /*这里为什么叫做accept,而不是priceChange等等这样更容易表达语意的函数名呢?
     * 1 accept对于熟悉设计模式的人,看到这个名称很容易构思到本模式。所以容易理解
     * 
     * 2 我很难想出其他名称,因为接下来的举例你会发现有可能我要实现需求的另一个维度变化
     * 比方说显示车辆的最高时速。那么此时priceChange方法名称反而容易让使用者误导。【有时候模糊
     * 也是一种“美“ - - ,无奈之下,我选择尊重第一条原因吧】
    */
    public void accept(Vistor v);
}

//Vistor.java 
package com.xiaolu.visitpattern;

public interface Vistor {
    /* 这里有人疑惑 为什么不适用”面向接口编程“,写作
     * public void visit(Vehicle v); 呢? 嗯,你是一个爱思考的人,我也是- -)
     * 因为有时候 具体的子类可能有有些自己特殊的方法【比如飞机会飞...当然这样做会牺牲掉使用
     * Vehicle多态--不是很推荐,但现实中经常需要这样,如果你能确保不必如此,那也可以吧】
     * 
     * 为什么叫做visit而不是其他名称?原因基本和上面【对于为什么叫accept】一样
     */
    public void visit(Bus b);

    public void visit(Train t);
}

//GQVisitor.java --- 一个具体的访问者,代表国庆价格上调这种行为需求【原谅我可怜的英语水平】
package com.xiaolu.visitpattern;

public class GQVisitor implements Vistor {
    @Override
    public void visit(Bus b) {
        b.showPrice();
        System.out.println("国庆期间汽车价格上涨5%");
    }

    @Override
    public void visit(Train t) {
        t.showPrice();
        System.out.println("国庆期间价格上涨3%");
    }

}

//Bus.java
package com.xiaolu.visitpattern;

public class Bus implements Vehicle {
    @Override
    public void showPrice() {
        System.out.println("汽车当前票价为80元");
    }

    @Override
    public void accept(Vistor v) {
        v.visit(this); //对于此处,我不想做太多解释,我更希望理解语意【为什么要用访问者模式?】
                       //如果你不了解这句话,可以搜搜“双分派机制”或者算了,直接放弃这块吧。
                       //你OO功底不过关- - 。
    }
}
// Train.java
package com.xiaolu.visitpattern;

public class Train implements Vehicle {

    @Override
    public void showPrice() {
        System.out.println("火车当前票价为 50元");
    }

    @Override
    public void accept(Vistor v) {
        v.visit(this);
    }
}

//VisitPatternMain.java ---客户端
package com.xiaolu.visitpattern;

public class VisitPatternMain {
    public static void main(String[] args) {
        Bus b = new Bus();

        Train t = new Train();

        Vistor v =  new GQVisitor();
        v.visit(b);
        System.out.println("-------------------------------------");
        v.visit(t);
    }
}

输出:
汽车当前票价为80元
国庆期间汽车价格上涨5%
-------------------------------------
火车当前票价为 50元
国庆期间价格上涨3%

这个达到了我们的要求,那么此时,需要要求车辆可以显示自己的时速呢?

//RateVistor.java
package com.xiaolu.visitpattern;

public class RateVistor implements Vistor{

    @Override
    public void visit(Bus b) {
        System.out.println(b.getClass().getName() + "速度为80km/h");
    }

    @Override
    public void visit(Train t) {
        System.out.println(t.getClass().getName() + "速度为200km/h");
    }

}
//VisitPatternMain.java ----客户端
package com.xiaolu.visitpattern;

public class VisitPatternMain {
    public static void main(String[] args) {
        Bus b = new Bus();

        Train t = new Train();

        //Vistor v =  new GQVisitor(); //我只是修改了一句话,其实吧,我更想使用一个简单工厂直接
                                       //把此处封装起来。不管为了避免过多引入复杂性。忍忍咯- - 
        Vistor v = new RateVistor();
        v.visit(b);
        System.out.println("-------------------------------------");
        v.visit(t);
    }
}

输出:
com.xiaolu.visitpattern.Bus速度为80km/h
-------------------------------------
com.xiaolu.visitpattern.Train速度为200km/h

到这里你疑惑了,为什么没有出现”对象结构“?!就是那个可以遍历啊,枚举元素的东西【类似于java中的Collection的玩意?嗯,好吧,那就加上吧—-

“`
//VehicleCollection.java 充当”对象结构”
package com.xiaolu.visitpattern;

import java.util.LinkedList;
import java.util.List;

public class VehicleCollection {
private List vs = new LinkedList();

public void attach(Vehicle v) {
    if (v != null && !vs.contains(v)) {
        vs.add(v);
    }
}

public void detach(Vehicle v) {
    if (v != null && vs.contains(v)) {
        vs.remove(v);
    }
}

public void accept(Vistor v) {
    if (v == null)
        return;

    for (Vehicle vehicle : vs) {
        vehicle.accept(v);
    }
}

public void clear() {
    if (vs != null && !vs.isEmpty()) {
        vs.clear();
    }
}

}

//VisitPatternMain2.java —-客户端
package com.xiaolu.visitpattern;

public class VisitPatternMain2 {
public static void main(String[] args) {
VehicleCollection vc = new VehicleCollection();

    vc.attach(new Bus());
    vc.attach(new Train());

    vc.accept(new RateVistor());
}

}

输出:
com.xiaolu.visitpattern.Bus速度为80km/h
com.xiaolu.visitpattern.Train速度为200km/h

如你所感觉,在最后这里,我有一种“为了模式而模式”的无奈感【为了“完整性”还是实现了它】,我觉得最重要的是体会访问者模式这种思路,而不是它规定的必须“有什么“。当然也有可能是我个人
理解的问题,至少目前这个问题背景下,我觉得没有必要,反而”有害“,想想如果Bus或者Train类中恰好有一些自己的”特殊“行为,那么attch进入VehicleCollection后,全部丢失了- -,这有时候不是你想要的吧- -。

说了这么多,优点和缺点依照个人理解总结下:

优点:
有很大的灵活性,想通了原理后,实现起来也不算太麻烦。
缺点:
也可以算是它的不适用场景吧。就是如果”元素“经常有变动,那么就会导致Vistor的接口不断变动。然而无绝对,如果不考虑具体“元素”的特殊性行为,而是在Vistor中直接以 ”元素接口“实现,可能也不会带来太大冲击。【这个没有实践过,是个人推断】

以上是个人理解,如果有错误或者理解不到位的地方,非常欢迎讨论指出,交流使得我们更好的前进。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值