一、访问者模式定义
1.访问者模式是一种将数据结构与数据操作分离的设计模式,是指封装一些作用于某种数据结构中的各元素的操作,它可以在不改变数据结构的前提下定义作用于这些元素的新的操作。属于行为型模式。
2.访问者模式被称为最复杂的设计模式,并且使用频率不高,设计模式的作者也评价为:大多情况下,你不需要使用访问者模式,但是一旦需要使用它时,那就真的需要使用了。访问者模式的基本思想是,针对系统中拥有固定类型数的对象结构(元素),在其内提供一个accept()方法用来接受访问者对象的访问。不同的访问者对同一元素的访问内容不同,使得相同的元素集合可以产生不同的数据结果。accept()方法可以接收不同的访问者对象,然后在内部将自己(元素)转发到接收到的访问者对象的visit()方法内。访问者内部对应类型的visit()方法就会得到回调执行,对元素进行操作。也即是通过两次动态分发(第一次是对访问者的分发accept()方法,第二次是对元素的分发visit()方法),才最终将一个具体的元素传递到一个具体的访问者。如此一来,就解耦了数据结构与操作,且数据操作不会改变元素状态
3.访问者模式的核心是解耦数据结构与数据操作,使得对元素的操作具备优秀的扩展性。可以通过扩展不同的数据操作类型(访问者)实现对相同元素集的不同的操作
4.访问者模式的适用场景:
A.数据结构稳定,作用于数据结构的操作经常变化的场景
B.需要数据结构与数据操作分离的场景
C.需要对不同数据类型(元素)进行操作,而不使用分支判断具体类型的场景
二、访问者模式示例
1.访问者模式一般包含5种角色:
A.抽象访问者(Visitor):接口或抽象类
B.具体访问者(ConcreteVisitor):实现对具体元素的操作
C.抽象元素(Element):接口或抽象类,定义了一个接受访问者访问的方法accept(),表示所有元素类型都支持被访问者访问
D.具体元素(ConcreteElement):具体元素类型,提供接受访问者的具体实现,通常的实现都为:visitor.visit(this)
E.结构对象(ObjectStructure):该类内部维护了元素集合,并提供方法接受访问者对该集合所有元素进行操作
2.代码示例
1 // 抽象访问者 2 public interface IVisitor { 3 4 void visit(ConcreteElementA element); 5 6 void visit(ConcreteElementB element); 7 } 8 9 // 具体访问者 10 public class ConcreteVisitorA implements IVisitor { 11 12 public void visit(ConcreteElementA element) { 13 String result = element.operationA(); 14 System.out.println("result from " + element.getClass().getSimpleName() + ": " + result); 15 } 16 17 public void visit(ConcreteElementB element) { 18 int result = element.operationB(); 19 System.out.println("result from " + element.getClass().getSimpleName() + ": " + result); 20 } 21 } 22 23 // 具体访问者 24 public class ConcreteVisitorB implements IVisitor { 25 26 public void visit(ConcreteElementA element) { 27 String result = element.operationA(); 28 System.out.println("result from " + element.getClass().getSimpleName() + ": " + result); 29 } 30 31 32 public void visit(ConcreteElementB element) { 33 int result = element.operationB(); 34 System.out.println("result from " + element.getClass().getSimpleName() + ": " + result); 35 } 36 } 37 38 // 抽象元素 39 public interface IElement { 40 void accept(IVisitor visitor); 41 } 42 43 // 具体元素 44 public class ConcreteElementA implements IElement { 45 46 public void accept(IVisitor visitor) { 47 visitor.visit(this); 48 } 49 50 public String operationA() { 51 return this.getClass().getSimpleName(); 52 } 53 54 } 55 56 // 具体元素 57 public class ConcreteElementB implements IElement { 58 59 public void accept(IVisitor visitor) { 60 visitor.visit(this); 61 } 62 63 public int operationB() { 64 return new Random().nextInt(100); 65 } 66 } 67 68 // 结构对象 69 public class ObjectStructure { 70 private List<IElement> list = new ArrayList<IElement>(); 71 72 { 73 this.list.add(new ConcreteElementA()); 74 this.list.add(new ConcreteElementB()); 75 } 76 77 public void accept(IVisitor visitor) { 78 for (IElement element : this.list) { 79 element.accept(visitor); 80 } 81 } 82 } 83 84 public class Test { 85 86 public static void main(String[] args) { 87 ObjectStructure collection = new ObjectStructure(); 88 System.out.println("ConcreteVisitorA handle elements:"); 89 IVisitor visitorA = new ConcreteVisitorA(); 90 collection.accept(visitorA); 91 System.out.println("------------------------------------"); 92 System.out.println("ConcreteVisitorB handle elements:"); 93 IVisitor visitorB = new ConcreteVisitorB(); 94 collection.accept(visitorB); 95 } 96 97 }
3.访问者模式的优缺点
A.优点
a.解耦了数据结构和数据操作,使得操作集合可以独立变化
b.扩展性好:可以通过扩展访问者角色,实现对数据集的不同操作
c.元素具体类型并非单一,访问者均可操作
d.各角色职责分离,符合单一职责原则
B.缺点
a.无法增加元素类型:若系统数据结构对象易于变化,经常有新的数据对象增加进来,则访问者类必须增加对应元素类型的操作,违背了开闭原则
b.具体元素变更困难:具体元素增加属性,删除属性等操作会导致对应的访问者类需要进行相应的修改,尤其当有大量访问者类时,修改范围太大
c.违背依赖倒置原则:为了达到"区别对待",访问者依赖的是具体元素类型,而不是抽象