访问者模式简介 访问者模式是一种行为模式。
访问者模式是一种将数据操作和数据结构分离的设计模式。(觉得太抽象,可以看下面的例子)。
使用场景
对象结构比较稳定,但经常需要在此对象结构上定义新的操作。
需要对一个对象结构中的对象进行很多不同的并且不相关的操作,而需要避免这些操作“污染”这些对象的类,也不希望在增加新操作时修改这些类。
优缺点 优点
各角色职责分离,符合单一职责原则 通过后面UML类图和示例可以看出来,Visitor、ConcreteVisitor、Element 、ObjectStructure,职责单一,各司其责。
具有优秀的扩展性 如果需要增加新的访问者,增加实现类 ConcreteVisitor 就可以快速扩展。
使得数据结构和作用于结构上的操作解耦,使得操作集合可以独立变化 城市经典(数据结构)和跟团游游客、自驾游游客访问者(数据操作)的解耦。
灵活性
缺点
具体元素对访问者公布细节,违反了迪米特原则 团游游客、自驾游游客需要调用具体城市的方法。
具体元素变更时导致修改成本大 变更城市属性时,多个访问者都要修改,比如雷峰塔倒了。
违反了依赖倒置原则,为了达到“区别对待”而依赖了具体类,没有以来抽象 访问者 visit 方法中,依赖了具体城市的具体方法。
通用模板 UML 图
角色介绍
Visitor :接口或者抽象类,定义了对每个 Element 访问的行为,它的参数就是被访问的元素,它的方法个数理论上与元素的个数是一样的,因此,访问者模式要求元素的类型要稳定,如果经常添加、移除元素类,必然会导致频繁地修改 Visitor 接口,如果出现这种情况,则说明不适合使用访问者模式。
ConcreteVisitor :具体的访问者,它需要给出对每一个元素类访问时所产生的具体行为。 Element:元素接口或者抽象类,它定义了一个接受访问者(accept)的方法,其意义是指每一个元素都要可以被访问者访问。
ElementA、ElementB :具体的元素类,它提供接受访问的具体实现,而这个具体的实现,通常情况下是使用访问者提供的访问该元素类的方法。
ObjectStructure :定义当中所提到的对象结构,对象结构是一个抽象表述,它内部管理了元素集合,并且可以迭代这些元素提供访问者访问。
client : 具体的运行代码类
通用代码 Element接口
1 2 3 4 5 6 7 8 public interface Element { public void accept (Visitor visitor) ; }
Element Concrete 实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 public class ElementA implements Element { @Override public void accept (Visitor visitor) { visitor.visit(this ); } public void operation1 () { System.out.println("ElementA do operation1" ); } } public class ElementB implements Element { @Override public void accept (Visitor visitor) { visitor.visit(this ); } public void operation2 () { System.out.println("ElementB do operation2" ); } }
visitor接口
1 2 3 4 5 6 7 8 9 10 public interface Visitor { public void visit (ElementA element1) ; public void visit (ElementB element2) ; }
visitor concrete
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 public class VisitorA implements Visitor { @Override public void visit (ElementA elementA) { System.out.println("Visitor A start visit ElementA" ); elementA.operation1(); } @Override public void visit (ElementB elementB) { System.out.println("Visitor A start visit ElementB" ); elementB.operation2(); } } public class VisitorB implements Visitor { @Override public void visit (ElementA elementA) { System.out.println("Visitor B start visit ElementA" ); elementA.operation1(); } @Override public void visit (ElementB elementB) { System.out.println("Visitor B start visit ElementB" ); elementB.operation2(); } }
ObjectStructure
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 public class ObjectStructure { private List<Element> elementList; public ObjectStructure () { elementList=new ArrayList <Element>(); elementList.add(new ElementA ()); elementList.add(new ElementB ()); } public void accept (Visitor visitor) { for (Element element:elementList){ element.accept(visitor); } } public void addElement (Element element) { elementList.add(element); } public void removeElement (Element element) { elementList.remove(element); } }
最后client调用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public class Client { private Visitor visitor; private ObjectStructure objectStructure; public void doVisit () { ObjectStructure objectStructure=new ObjectStructure (); objectStructure.accept(new VisitorA ()); objectStructure.accept(new VisitorB ()); } public static void main (String[] args) { Client client=new Client (); client.doVisit(); } }
执行结果
1 2 3 4 5 6 7 8 Visitor A start visit ElementA ElementA do operation1 Visitor A start visit ElementB ElementB do operation2 Visitor B start visit ElementA ElementA do operation1 Visitor B start visit ElementB ElementB do operation2
依然很抽象,看下面的例子吧。
旅游城市例子 访问者模式,用游客访问城市经典的例子再合适不过了.
不同的游客有不同的访问行为,跟团游和自驾游或休闲游在游览景点是的行为也是完全不相同的,完美符合访问者模式中的访问者这一对象。
UML 图
角色介绍
City 城市充当Element角色,各城市属性非常稳定,但是城市所属的景点可能发生轻微改造或变更,完美符合Element角色的要求。
HangzhouCity,HuangshanCity,BeijingCity 三个城市的具体实现:杭州、黄山、北京,对应的是ElementConcrete
Tourists 游客接口,游客可以访问城市,对应访问者模式中的Visitor角色。
WithTheGroupTourists,SelfDriverTourists 游客的两个具体实现,跟团游游客和自驾游游客,对应VisitorConcrete
TravelPlan 旅游计划抽象,封装了一堆城市的集合。 对应ObjectStructure
TravelClient 具体的一次旅游,对应 Client
实例代码 City
1 2 3 4 5 6 7 8 public interface City { public void accept (Tourists visitor) ; }
CityConcrete
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 public class BeijingCity implements City { @Override public void accept (Tourists tourists) { tourists.visitBeijing(this ); } public String greatWall () { return "长城" ; } public String forbiddenCity () { return "故宫" ; } public String houhaiBar () { return "后海酒吧" ; } } public class HangzhouCity implements City { @Override public void accept (Tourists tourists) { tourists.visitHangzhou(this ); } public String westLake () { return "西湖" ; } public String zhejiangUniversity () { return "浙江大学" ; } public String leifengPagoda () { return "雷峰塔" ; } } public class HuangshanCity implements City { @Override public void accept (Tourists tourists) { tourists.visitHuangshan(this ); } public String huangshan () { return "黄山" ; } }
Tourists
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 public interface Tourists { public void visitBeijing (BeijingCity beijingCity) ; public void visitHangzhou (HangzhouCity hangzhouCity) ; public void visitHuangshan (HuangshanCity huangshanCity) ; }
TouristsConcrete
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 public class SelfDriveTourists implements Tourists { @Override public void visitBeijing (BeijingCity beijingCity) { System.out.println(">>>>>开始自驾旅游北京" ); System.out.println("第1站:" +beijingCity.greatWall()); System.out.println("第2站:" +beijingCity.forbiddenCity()); System.out.println("第3站:" +beijingCity.houhaiBar()); System.out.println(">>>>>结束自驾旅游北京" ); } @Override public void visitHangzhou (HangzhouCity hangzhouCity) { System.out.println(">>>>>开始自驾旅游杭州" ); System.out.println("第1站:" +hangzhouCity.westLake()); System.out.println("第2站:" +hangzhouCity.leifengPagoda()); System.out.println("第3站:" +hangzhouCity.zhejiangUniversity()); System.out.println(">>>>>结束自驾旅游杭州" ); } @Override public void visitHuangshan (HuangshanCity huangshanCity) { System.out.println(">>>>>开始自驾旅游黄山" ); System.out.println("第1站:" +huangshanCity.huangshan()); System.out.println(">>>>>结束自驾旅游黄山" ); } } public class WithTheGroupTourists implements Tourists { @Override public void visitBeijing (BeijingCity beijingCity) { System.out.println(">>>>>开始跟团旅游北京" ); System.out.println("第1站:" +beijingCity.houhaiBar()); System.out.println("第2站:" +beijingCity.forbiddenCity()); System.out.println("跟团游时间太紧,无法旅游:" +beijingCity.greatWall()); System.out.println(">>>>>结束跟团旅游北京" ); } @Override public void visitHangzhou (HangzhouCity hangzhouCity) { System.out.println(">>>>>开始跟团旅游杭州" ); System.out.println("第1站:" +hangzhouCity.westLake()); System.out.println("第2站:" +hangzhouCity.zhejiangUniversity()); System.out.println("跟团游时间太紧,无法旅游:" +hangzhouCity.leifengPagoda()); System.out.println(">>>>>结束跟团旅游杭州" ); } @Override public void visitHuangshan (HuangshanCity huangshanCity) { System.out.println(">>>>>开始跟团旅游黄山" ); System.out.println("第1站:" +huangshanCity.huangshan()); System.out.println(">>>>>结束跟团旅游黄山" ); } }
TravelPlan
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 public class TravelPlan { private List<City> cityList; public TravelPlan () { cityList=new ArrayList <City>(); cityList.add(new BeijingCity ()); cityList.add(new HangzhouCity ()); cityList.add(new HuangshanCity ()); } public void travel (Tourists visitor) { for (City city:cityList){ city.accept(visitor); } } public void addCity (City city) { cityList.add(city); } public void removeCity (City city) { cityList.remove(city); } }
TravelClient
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 public class TravelClient { private Tourists visitor; private TravelPlan travelPlan; public void doTravel () { TravelPlan travelPlan=new TravelPlan (); travelPlan.travel(new WithTheGroupTourists ()); System.out.println(); System.out.println(); travelPlan.travel(new SelfDriveTourists ()); } public static void main (String[] args) { TravelClient travelClient=new TravelClient (); travelClient.doTravel(); } }
执行结果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 > >>>>开始跟团旅游北京 第1站:后海酒吧 第2站:故宫 跟团游时间太紧,无法旅游:长城 > >>>>结束跟团旅游北京 > >>>>开始跟团旅游杭州 第1站:西湖 第2站:浙江大学 跟团游时间太紧,无法旅游:雷峰塔 > >>>>结束跟团旅游杭州 > >>>>开始跟团旅游黄山 第1站:黄山 > >>>>结束跟团旅游黄山 > >>>>开始自驾旅游北京 第1站:长城 第2站:故宫 第3站:后海酒吧 > >>>>结束自驾旅游北京 > >>>>开始自驾旅游杭州 第1站:西湖 第2站:雷峰塔 第3站:浙江大学 > >>>>结束自驾旅游杭州 > >>>>开始自驾旅游黄山 第1站:黄山 > >>>>结束自驾旅游黄山
代码 以上例子代码可在个人github项目design-patterns 中找到。
参考文章 访问者模式一篇就够了