設計模式之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

三、總結

訪客模式的優缺點:這里不做總結,百度很多。

事例中的優缺點:

優點:菜品的新增,直接增加類就可以了;

          訪客的變化,直接增加類實現接口就可以了;

          制定新的銷售手段,直接增加下單類就可以了;

          這符合“開閉原則”。

缺點:很明顯,不能對菜品進行修改。

四、與本章節無關

本人技術有限,如果錯誤或者不合理的地方,歡迎聯系我整改。

突然寫此章節,是一個偶然的機會,想到了這個有趣的設計模式例子,就寫了下來。后續如果生活中發現符合其它設計模式的有趣例子,會持續增加。

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM