前端设计模式应用--访问者模式

117 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第19天

介绍

  • 表示一个作用于某对象结构中的各元素的操作。它使你可以在不改变各元素类的前提下定义作用于这些元素的新操作。

    在访问者模式(Visitor Pattern)中,我们使用了一个访问者类,它改变了元素类的执行算法。通过这种方式,元素的执行算法可以随着访问者改变而改变。这种类型的设计模式属于行为型模式。根据模式,元素对象已接受访问者对象,这样访问者对象就可以处理元素对象上的操作。

    访问者模式 是一种将算法与对象结构分离的设计模式,通俗点讲就是:访问者模式让我们能够在不改变一个对象结构的前提下能够给该对象增加新的逻辑,新增的逻辑保存在一个独立的访问者对象中。访问者模式常用于拓展一些第三方的库和工具。

使用场景

  • 对象结构中对象对应的类很少改变,但经常需要在此对象结构上定义新的操作。

  • 需要对一个对象结构中的对象进行很多不同的并且不相关的操作,而需要避免让这些操作"污染"这些对象的类,也不希望在增加新操作时修改这些类。

应用实践

职责明确

  • Visitor Object:访问者对象,访问者接口。拥有一个visit()方法

  • Receiving Object:具体的访问者。接收对象,拥有一个accept() 方法

  • visit(receivingObj):可以被访问者使用的元素,它必须定义一个 Accept 属性,接收 visitor 对象。这是实现访问者模式的关键。,用于Visitor接收一个Receiving Object

  • accept(visitor):对象结构,存储了多个 Element,利用 Visitor 进行批量操作。用于Receving Object接收一个Visitor,并通过调用Visitorvisit() 为其提供获取Receiving Object数据的能力

实现

// 访问者模式:DOM事件绑定
var bindEvent = function (dom, type, fn, data) {
    if (dom.addEventListener) {
        dom.addEventListener(type, fn, false);
    } else if (dom.attachEvent) {
        // dom.attachEvent('on'+type, fn);
        var data = data || {};
        dom.attachEvent('on' + type, function (e) {
            // 在IE中this指向 window,使用call改变this的指向
            fn.call(dom, e, data);
        });
    } else {
        dom['on' + type] = fn;
    }
}

function $(id) {
    return document.getElementById(id);
}​
bindEvent($(demo), 'click', function () {
    // this 指向dom对象
    this.style.background = 'red';
});​
bindEvent($('btn'), 'click', function (e, data) {
    $('text').innerHTML = e.type + data.text + this.tagName;
}, {
    text: 'demo'
});


var Visitor = (function () {
    return {
        splice: function () {
            var args = Array.prototype.splice.call(arguments, 1);
            return Array.prototype.splice.apply(arguments[0], args);
        },
        push: function () {
            var len = arguments[0].length || 0;
            var args = this.splice(arguments, 1);
            arguments[0].length = len + arguments.length - 1;
            return Array.prototype.push.apply(arguments[0], args);
        },
        pop: function () {
            return Array.prototype.pop.apply(arguments[0]);
        }
    }
})();​
var a = new Object();
Visitor.push(a, 1, 2, 3, 4);
Visitor.pop(a);
Visitor.splice(a, 2);

汇总

  • 准确识别出Visitor实用的场景,如果一个对象结构不稳定决不可使用,不然在增删元素时改动将非常巨大。

  • 对象结构中的元素要可以迭代访问

  • Visitor里一般存在与元素个数相同的visit方法。

  • 元素通过 this 方法通过 accept 将自己传递给了Visitor。

优点特性

  • 使得数据结构和作用于结构上的操作解耦,使得操作集合可以独立变化。

  • 添加新的操作或者说访问者会非常容易。

  • 将对各个元素的一组操作集中在一个访问者类当中。

  • 使得类层次结构不改变的情况下,可以针对各个层次做出不同的操作,而不影响类层次结构的完整性。

缺点弊端

  • 具体元素对访问者公布细节,违反了迪米特原则

  • 增加新的元素类很困难,在访问者模式下,每增加一个新的元素,对应要增加访问者中的操作。

  • 违反了依赖倒置原则,依赖了具体类,没有依赖抽象。破坏封装。访问者模式要求访问者对象访问并调用每一个元素对象的操作,这意味着元素必须暴露一些内部操作和内部状态