設計模式,現在在網上隨便搜都一大堆,為什么我還要寫“設計模式”的章節呢?
兩個原因:
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
三、總結
訪客模式的優缺點:這里不做總結,百度很多。
事例中的優缺點:
優點:菜品的新增,直接增加類就可以了;
訪客的變化,直接增加類實現接口就可以了;
制定新的銷售手段,直接增加下單類就可以了;
這符合“開閉原則”。
缺點:很明顯,不能對菜品進行修改。
四、與本章節無關
本人技術有限,如果錯誤或者不合理的地方,歡迎聯系我整改。
突然寫此章節,是一個偶然的機會,想到了這個有趣的設計模式例子,就寫了下來。后續如果生活中發現符合其它設計模式的有趣例子,會持續增加。