设计模式之visitor模式,人人能懂的有趣实例

设计模式,现在在网上随便搜都一大堆,为什么我还要写“设计模式”的章节呢?

两个原因

1.本人觉得这是一个有趣的设计模式使用实例,所以记下来;

2.看着设计模式很牛逼,却不知道怎么在实战中应用出来。希望这个实例能给学习者一个参考,一点启发。

注意:本文是个人对设计模式的见解,不会出现大家常见的设计模式的概念。此文只作为一个实例。建议初学者参杂着别人博文一起读。

在此,向《大话设计模式》作者吴强前辈致敬

一、 Visitor(访问者)模式

关键词:访问者, 被访问者(元素),元素群,双重注入,操作

访问者模式的作用:对【元素】、【访问者】和【对元素的操作】进行单独抽象和封装,一旦这些【访问者】的增加或者是【对元素的操作】发生变化,不需要修改【元素】。

 

二、故事背景

程序员小明到了新的软件园,呆了半个月之后,发现附近没有快餐店,人流特别多。于是决定投资开一家快餐店,参考"某乐园"的运营模式。快餐店很快就装修好了。小明参考各路快餐店,很快就设计出了几款菜式。经过深思熟虑,小明对点餐计费环节,用了Visitor模式。

三、设计分析

每道菜(为了直观,这里没用商品,而是把汤和饭也作为菜品之一)作为最小的单元(元素);

每个点餐的客户作为访问者;

每一份快餐的菜单,则归类为对菜的操作。

四、参考事例

1.菜的抽象父类

public abstract class Element {
    protected String name; //菜名
    protected float price; //价格
    protected int weight;    //份量
    abstract public void 供给(Visitor visitor);
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public float getPrice() {
        return price;
    }
    public void setPrice(float price) {
        this.price = price;
    }
    public int getWeight() {
        return weight;
    }
    public void setWeight(int weight) {
        this.weight = weight;
    }
    
}

2.食客接口

public interface Visitor {
    abstract public void 选菜( Element element );
}

3.各种菜品继承菜的父类

/**
 * 卷心菜
 * @author 南城草
 *
 */
public class Cabbage extends Element {

    public Cabbage(int weight) {
        this();
this.weight = weight;
this.price = price*weight; } public Cabbage() { this.name = "卷心菜"; this.price = 2; this.weight = 1; } @Override public void 供给(Visitor visitor) { visitor.选菜(this); } }
/**
 * 饭
 * @author 南城草
 *
 */
public class Meal extends Element {

    public Meal(int weight) {
        this();
     this.weight = weight;
this.price = price*weight; } public Meal() { this.name = "饭"; this.price = 2; this.weight = 1; } @Override public void 供给(Visitor visitor) { visitor.选菜(this); } }
/**
 * 烧鸭
 * @author 南城草
 *
 */
public class RoastDuck extends Element {

    public RoastDuck(int weight) {
        this();
this.weight = weight;
this.price = price*weight; } public RoastDuck() { this.name = "烧鸭"; this.price = 8; this.weight = 1; } @Override public void 供给(Visitor visitor) { visitor.选菜(this); } }
/**
 * 叉烧
 * @author 南城草
 *
 */
public class RoastPork extends Element {

    public RoastPork(int weight) {
        this();
this.weight = weight;
this.price = price*weight; } public RoastPork() { this.name = "叉烧"; this.price = 8; this.weight = 1; } @Override public void 供给(Visitor visitor) { visitor.选菜(this); } }
/**
 * 汤
 * @author 南城草
 *
 */
public class Soup extends Element {

    public Soup(int weight) {
        this();
this.weight = weight;
this.price = price*weight; } public Soup() { this.name = "汤"; this.price = 3; this.weight = 1; } @Override public void 供给(Visitor visitor) { visitor.选菜(this); } }

……后面省略了快餐店多种菜式

4.普通客户 实现访客者(Visitor)接口

/**
 * 普通客户
 * @author 南城草
 *
 */
public class NormalVisitor implements Visitor {

    @Override
    public void 选菜(Element element) {
        Element food = ((Element) element);
        System.out.println(food.getName() +food.getWeight()+ "份!");
    }

}

……后面省略多种不同客户的优惠实现(什么会员用户打折,会员积分都在这里拓展)

5.下单类 

/**
 * 普通菜单
 * @author 南城草
 *
 */
public class Lunchbox {
    private HashMap<String, Element> elements;
    private float allPrice = 0;

    public Lunchbox() {
        elements = new HashMap();
    }

    public void Attach(Element element) {
        elements.put(element.getName(), element);
    }

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

    public Element getElemente(String name) {
        return elements.get(name);
    }

    public void Accept(Visitor visitor) {
        for (Element e : elements.values()) {
            e.供给(visitor);
            allPrice += e.getPrice() * e.getWeight();
        }
        System.out.print("总价:"+allPrice);
    }
}

……这里省略了多种点餐单,什么节日满减,都可以在这里拓展

5.测试类——一个普通用户过来,要点菜啦

public class MainTest {
    public static void main(String[] args) {
        Lunchbox lunchbox = new Lunchbox();

        lunchbox.Attach(new RoastDuck(1));
        lunchbox.Attach(new Meal(2));
        lunchbox.Attach(new Soup(1));

        lunchbox.Accept(new NormalVisitor());
    }

}

6.测试结果

饭1份!
烧鸭1份!
汤1份!
总价:15.0

三、总结

访客模式的优缺点:这里不做总结,百度很多。

事例中的优缺点:

优点:菜品的新增,直接增加类就可以了;

          访客的变化,直接增加类实现接口就可以了;

          制定新的销售手段,直接增加下单类就可以了;

          这符合“开闭原则”。

缺点:很明显,不能对菜品进行修改。

四、与本章节无关

本人技术有限,如果错误或者不合理的地方,欢迎联系我整改。

突然写此章节,是一个偶然的机会,想到了这个有趣的设计模式例子,就写了下来。后续如果生活中发现符合其它设计模式的有趣例子,会持续增加。

 

 

posted @ 2017-10-19 15:38  南城草  阅读(5980)  评论(3编辑  收藏  举报