访问者模式
对象中的元素,在不更改对象的前提下访问结构中的元素
角色
- 访问者:自定义实现访问对象的方法,如果访问的对象有多个则分别实现
- 被访问者:定义接待方法,接收访问者,调用访问者对应的访问方法
应用场景
- 一个对象结构包含多种类型的对象,希望对这些对象实施一些依赖其具体类型的操作。在访问者中针对每一种具体的类型都提供了一个访问操作,不同类型的对象可以有不同的访问操作。
- 需要对一个对象结构中的对象进行很多不同的并且不相关的操作,而且需要避免让这些操作“污染”这些对象的类,也不希望在增加新操作时修改这些类。访问者模式将相关的访问操作集中起来定义在访问者类中,对象结构可以被多个不同的访问者类所使用,将对象本身与对象的访问操作分离。
- 对象结构中元素对象对应的类很少改变,但经常需要在此对象结构上定义新的操作。
实例:电脑
有一台电脑,有三部分组成,CPU、memery、board
有三种客户:学生、上班族、公司,针对不同的客户采用不同的优惠策略。
首先分别实现CPU、memery、board类组装成电脑,当访问者来的时候,电脑调用接待方法,CPU、memery、board分别接待访问者,访问者自己实现访问CPU、memery、board的方法。CPU、memery、board的接待方法只需要接收一个访问者,调用对应的访问方法即可
let CPU = function(){
this.price = 10;
}
CPU.prototype.getPrice = function () {
return this.price;
}
CPU.prototype.accept = function (v){
v.visitCpu(this);
}
let Memery = function (){
this.price = 15
}
Memery.prototype.getPrice = function () {
return this.price;
}
Memery.prototype.accept = function(v){
v.visitMemery(this);
}
let Board = function(){
this.price = 20;
}
Board.prototype.getPrice = function (){
return this.price;
}
Board.prototype.accept = function(v){
v.visitBoard(this);
}
let Computer = function (){
this.cpu= new CPU();
this.memery = new Memery();
this.board = new Board();
}
Computer.prototype.accept = function (v){
this.cpu.accept(v);
this.memery.accept(v);
this.board.accept(v);
}
let studentVisitor = function() {
this.totalPrice = 0;
}
studentVisitor.prototype.visitCpu = function (cpu) {
this.totalPrice += cpu.getPrice() * 0.9;
}
studentVisitor.prototype.visitMemery = function (memery){
this.totalPrice += memery.getPrice()*0.95;
}
studentVisitor.prototype.visitBoard = function (board){
this.totalPrice += board.getPrice() * 0.8;
}
优缺点
优点
- 增加新的访问操作很方便。使用访问者模式,增加新的访问操作就意味着增加一个新的具体访问者类,实现简单,无须修改源代码,符合开闭原则。
- 将有关元素对象的访问行为集中到一个访问者对象中,而不是分散在一个个的元素类中。类的职责更加清晰,有利于对象结构中元素对象的复用,相同的对象结构可以供多个不同的访问者访问。
- 让用户能够在不修改现有元素类层次结构的情况下,定义作用于该层次结构的操作。
缺点
- 增加新的元素类很困难。在访问者模式中,每增加一个新的元素类都意味着要在抽象访问者角色中增加一个新的抽象操作,并在每一个具体访问者类中增加相应的具体操作,这违背了开闭原则的要求。
- 破坏封装。访问者模式要求访问者对象访问并调用每一个元素对象的操作,这意味着元素对象有时候必须暴露一些自己的内部操作和内部状态,否则无法供访问者访问。