js设计模式-访问者模式

访问者模式定义

介绍:访问者模式比较复杂,它包含访问者和被访问元素两个主要组成部分,这些被访问的元素通常具有不同的类型,且不同的访问者可以对他们进行不同的访问操作。访问者模式的主要目的是将数据结构与数据操作相分离。

定义:提供一个作用于某对象结构中的个元素的操作表示,它使得可以再不改变各元素的类的前提下定义作用于这些元素的新操作。访问者模式是一种对象行为型模式

在访问者模式中,主要包括以下几个角色

  1. 抽象访问者:抽象类或者接口,声明访问者可以访问哪些元素,具体到程序中就是visit方法中的参数定义哪些对象是可以被访问的。
  2. 访问者:实现抽象访问者所声明的方法,它影响到访问者访问到一个类后该干什么,要做什么事情。
  3. 抽象元素类:接口或者抽象类,声明接受哪一类访问者访问,程序上是通过accept方法中的参数来定义的。抽象元素一般有两类方法,一部分是本身的业务逻辑,另外就是允许接收哪类访问者来访问。
  4. 元素类:实现抽象元素类所声明的accept方法,通常都是visitor.visit(this),基本上已经形成一种定式了。
  5. 结构对象:一个元素的容器,一般包含一个容纳多个不同类、不同接口的容器,如List、Set、Map等,在项目中一般很少抽象出这个角色。

示例:

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
var Keyboard = function(){
this.accept = function(computerPartVisitor){
computerPartVisitor.visit(this);
}
}

var Monitor = function(){
this.accept = function(computerPartVisitor){
computerPartVisitor.visit(this);
}
}

var Mouse = function(){
this.accept = function(computerPartVisitor){
computerPartVisitor.visit(this);
}
}

var Computer = function(){
var parts = [new Keyboard(), new Monitor(), new Mouse()];

this.accept = function(computerPartVisitor){
computerPartVisitor.visit(this);
_acceptAll(computerPartVisitor);
}

var _acceptAll = function(computerPartVisitor){
parts.map(function(item){
item.accept(computerPartVisitor)
});
}
}

var ComputerPartDisplayVisitor = function(){
this.visit = function(item){
if(item.constructor == Keyboard){
console.log("Displaying Keyboard.");
}else if(item.constructor == Monitor){
console.log("Displaying Monitor.");
}else if(item.constructor == Mouse){
console.log("Displaying Mouse.");
}else if(item.constructor == Computer){
console.log("Displaying Computer.");
}else{
console.log("Error");
}
}
}

var computer = new Computer();
computer.accept(new ComputerPartDisplayVisitor());
// Displaying Computer.
// Displaying Keyboard.
// Displaying Monitor.
// Displaying Mouse.

简洁版:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 访问者  
function Visitor() {
this.visit = function( concreteElement ) {
concreteElement.doSomething();
}
}
// 元素类
function ConceteElement() {
this.doSomething = function() {
console.log("这是一个具体元素");
}
this.accept = function( visitor ) {
visitor.visit(this);
}
}
// Client
var ele = new ConceteElement();
var v = new Visitor();
ele.accept( v );

这个模式比较复杂,我们先介绍下组成:
ComputerPartDisplayVisitor称为访问者,它为对象结构中的每一个具体元素例如Keyboard,Mouse等声明一个访问操作。当访问Keyboard时就会输出Displaying Keyboard。
Keyboard,Monitor,Mouse称为元素,他们包含一个accept方法,用来触发传递进来的访问者
Computer称为对象结构,它是一个元素的集合。parts数组用于存放元素对象,以供不同访问者访问。_acceptAll方法用来遍历内部元素。
访问者通过accept访问元素内部,元素内部也可以通过参数调用访问者的visit方法。这种调用机制称为双重分派。

访问者模式总结

优点:

  • 增加新的访问操作便捷,只需要加一个新的访问者类。
  • 将有关原色对象的访问行为集合到一个访问者对象中,类的职责更清晰。
  • 让用户在不修改现有元素层次结构的情况下,定义作用于该层次结构的操作。
    缺点:
  • 增加新的元素类很困难,在访问者模式下,每增加一个新的元素,对应要增加访问者中的操作。
  • 破坏封装。访问者模式要求访问者对象访问并调用每一个元素对象的操作,这意味着元素必须暴露一些内部操作和内部状态。

适用场景:

  • 一个对象结构包含多个类型的对象,希望对这些对象实施一些依赖其具体类型的操作。
  • 需要对一个对象结构中的对象进行横多不同的并且不相关的操作,而且需要避免让这些操作污染这些对象的类。
  • 对象结构中对象对应的类很少改变,但经常需要再次对象结构上定义新的操作。
Buy me a cup of coffee,thanks!