设计模式之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
三、总结
访客模式的优缺点:这里不做总结,百度很多。
事例中的优缺点:
优点:菜品的新增,直接增加类就可以了;
访客的变化,直接增加类实现接口就可以了;
制定新的销售手段,直接增加下单类就可以了;
这符合“开闭原则”。
缺点:很明显,不能对菜品进行修改。
四、与本章节无关
本人技术有限,如果错误或者不合理的地方,欢迎联系我整改。
突然写此章节,是一个偶然的机会,想到了这个有趣的设计模式例子,就写了下来。后续如果生活中发现符合其它设计模式的有趣例子,会持续增加。