桥接&组合

桥接

完成 API 的时刻,桥接形式异常有效,可以恰是由于这个,该形式运用地不够普遍.在设想 js API 时,该形式可以弱化API 与运用它的类和对象之间的耦合.
该形式的作用在于**将笼统与其完成隔脱离,让他们自力变化**.而且关于事宜驱动编程有很多优点.

js API有 getter, setter, requester 以及其他基于行动的要领.不管它们是用来建立 Web 鼓励 API 照样一般 accessor 和 mutator.在完成历程中桥接形式都有助于坚持 API 代码的简约.

事宜监听器

最常见的运用场所之一是事宜监听回调.假设有一个 API 函数 getBeerById, 他依据一个标识符返回有关啤酒的信息.

addEvent(ele, 'click', getBeerById);
function getBeerById(e) {
    var id = this.id;
    asyncRequest('GET', 'beer.url?id=' + id, function(resp) {
        // Callback response.
        console.log('Requested Beer: ' + resp.responseText);
    });
}

这个 API 只能事变在浏览器中,依据事宜监听器回调函数的机制,事宜对象天然会被作为第一个参数通报给这个函数,在本例中没有运用这个参数,只是从 this 对象猎取 id.那末假如在敕令行环境运转它就失效不起作用.

function getBeerById(id, callback) {
    // Make request for beer by ID, then return the beer data.
    asyncRequest('GET', 'beer.url?id=' + id, function (resp) {
        // callback reponse
        callback(resp.responseText);
    });
}

addEvent(ele, 'click', getBeerByIdBridge);
function getBeerByIdBridge(e) {
    getBeerById(this.id, function (beer) {
        console.log('Requested Beer: ' + beer);
    });
}

如许就只针对接口而不是完成举行编程,用桥接形式把笼统隔脱离来.
有了这层桥接元素,API 的实用范围大大拓宽,如今的 getBeerById 没有和事宜对象绑定在一同,可以在单元测试中运转该 API,只需供应一个 ID 和对换函数.如今可以在敕令行环境运转这个接口.

其他例子

除了在事宜回调函数与接口之间举行桥接外,桥接形式还可以用衔接公然的 API代码和私有的完成代码.别的,还可以衔接多个类.从类的角度看,意味着把接口作为公然的代码编写,把类的完成作为私有代码编写.
可以运用一些特权要领来做桥梁以便接见私有变量空间.第3章已见到过.

var Public = function () {
    var secretNum = 3;
    this.privilegedGetter = function () {
        return secretNum;
    };
};
var p = new Public();
var data = p.privilegedGetter();

衔接多个类:

var Class1 = function (a, b, c) {
    this.a = a;
    this.b = b;
    this.c = c;
};
var Class2 = function (d) {
    this.d = d;
}
var BridgeClass = function (a, b, c, d) {
    this.one = new Class1(a, b, c);
    this.two = new Class2(d);
}

在这里,桥接类是一个门面类,差别的是桥接形式可以让Class1和 Class2自力于 BridgeClass 而发生变化,和门面类差别.

实用场所

事宜驱动编程必备.不过我们喜好事宜驱动开辟的函数式作风,却忘了编写接口.推断什么时刻是用桥接形式很简朴:

$('#example-id').on('click', function () {
    new RichTextEditor();
})l

如今没法看出编辑器要显现在哪,有什么设置项,另有怎样修正.要做的是让接口’可桥接'(可适配)…

另有就是之前讲到的特权函数用来接见私有数据.

让 API 更强健,进步组件的模块化水平.把笼统和其完成隔脱离,有助于自力治理,而且Bug 更轻易查找.

每运用一个桥接元素都要增添一次函数挪用,进步系统庞杂度,影响机能,不要滥用,举例来讲,假如一个桥接函数被用于衔接两个函数,然则个中某个函数基础不会在桥接函数以外被挪用,那末该桥接函数可以没必要.

———-next part———-

组合

统一条敕令在多个对象上激发庞杂或许递归的操纵.
两大优点:

  1. 用一样的要领处置惩罚对象鸠合和个中的特定子对象.组合对象 composite 和组成它的对象完成了同批操纵.是一种向下通报的操纵性子.

  2. 把一批子对象构造成树形构造,使整棵树都可以被遍历.一切组合对象都完成了一个用来猎取子对象的要领.

组合对象的构造

组合对象的条理系统中有两种范例的对象:恭弘=叶 恭弘对象和组合对象,是一个递归定义:一个组合对象由一些别的组合对象和恭弘=叶 恭弘对象组成.只需恭弘=叶 恭弘对象不再包括子对象,它是组合对象中最基础的元素,也落实了种种操纵.(树是数据构造中较为基础的观点,这里的观点应当不难理解的)

运用

必需同时具有两个前提:

  • 存在一批构造成某种条理系统的对象

  • 愿望对这批对象或许个中一部分对象举行一个配合操纵.
    组合形式擅长于对多量对象举行操纵.构造这类对象并把操纵从一个条理向下一个条理通报,可以弱化对象间的耦合而且可以交流的运用一些类或许实例.

示例:表单考证

要求建立一个表单,可以保留,恢复,考证值.看似不庞杂,然则重要问题在于表单中元素的内容和数量完整未知,而且因用户而异.严密耦合到 Name 和 Address 特定表单域的 validate 函数不会起作用,由于没法考证哪些域…
如今是组合形式大展技艺的时刻,起首,我们要逐一考证表单的各个组成元素,推断属于组合对象照样恭弘=叶 恭弘对象.表单最基础的组成要素是用户用于输入数据的域,由input, select,textarea 等等组成.上面一层是用于构造域的 fieldset 标签.最顶层是表单本身.

恭弘=叶 恭弘对象 (input)   --|                        |
                   -- 组合对象 (filedset)    |
恭弘=叶 恭弘对象 (input)   --|                        |
                                           |=组合对象 (form)
恭弘=叶 恭弘对象 (select)  --|                        |
                   --组合对象 (fieldset)     |
恭弘=叶 恭弘对象 (textarea)--|                        |

组合对象和其一切子对象都具有雷同的接口,可以有人会把他们算作超类和子类的关联,然则并非如此,恭弘=叶 恭弘对象没有继续上一级组合对象.

起首是建立一个动态表单而且完成save 和 validate 操纵.表单中现实具有的域因用户而异,所以 save,validate 函数不可以满足每一个人须要,不过可以设想一个模块化表单,以便未来任何时刻都能为其增加种种元素,没必要修正 save 和 validate 函数.

没必要为表单元素的每一种可以组合编写一套要领,应当让这两个要领和表单域本身关联起来.让每一个域都可以保留和考证本身:

nameFieldset.validate();
nameFieldset.save();

难点在于怎样同时在一切域上实行这些操纵,运用组合形式来简化代码:要保留一切域,只需一次挪用:topForm.save();topForm 对象将会在一切子对象上递归挪用 save 要领,现实的 save 操纵只会发生在底层的恭弘=叶 恭弘对象上.组合对象志气到了一个通报挪用的作用.
完成代码:
起首,建立组合对象和恭弘=叶 恭弘对象须要完成的两个接口:

var Composite = new Interface('Composite', ['add', 'remove', 'getChild']);
var FormItem = new Interface('FormItem', ['save']);

现在 FormItem 只要求完成一个 save 函数,稍后会对其举行扩大.
CompositeForm 类的代码以下:

var CompositeForm = function(id, method, action) { // implements Composite, Formitem
    this.formComponents = [];

    this.element = document.createElement('form');
    this.element.id = id;
    this.element.method = method || 'POST';
    this.element.action = action || '#';
};

CompositeForm.prototype.add = function (child) {
    Interface.ensureImplements(child, Composite, FormItem);
    this.formComponents.push(child);
    this.element.appendChild(child.getElement());
};

CompositeForm.prototype.remove = function (child) {
    for (var i = 0, len = this.formComponents.length; i < len; i++) {
        if (this.formComponents[i] === child) {
            this.formComponents.splice(i, 1); // Remove one element from the array at position i.
            break;
        }
    }
};

CompositeForm.prototype.getChild = function (i) {
    return this.formComponents[i];
};
CompositeForm.prototype.remove = function (child) {
    for (var i = 0, len = this.formComponents.length; i < len; i++) {
        this.formComponents[i].save();
    }
};

CompositeForm.prototype.getElement = function () {
    return this.element;
};

CompositeForm 的子对象保留在一个数组中.运用 Interface.ensureImplements 是为了保证要增加到组合对象中的对象完成了准确的接口.
如今看看恭弘=叶 恭弘对象类:

var Field = function (id) { // implements Composite, FormItem
    this.id = id;
};

Field.prototype.add = function () {};
Field.prototype.remove = function () {};
Field.prototype.getChild = function () {};

Field.prototype.save = function () {
    setCookie(this.id, this.getValue());
};

Field.prototype.getElement = function () {
    return this.element;
};

Field.prototype.getValue = function () {
    throw new Error('Unsupported operation on the class Field.');
};

这个类将被各个恭弘=叶 恭弘对象类继续.它将 Composite 接口中的要领完成为空函数,由于恭弘=叶 恭弘节点不会有子对象.

这里最简朴地完成了 save 要领.然则把用户原始数据放在 Cookie 是一个异常蹩脚的做法.由于 Cookie 很轻易被改动,所以数据的有效性得不到保证.其次,存储在 Cookie 中的数据有大小限定.所以用户的数据可以不会被悉数保留下来.
末了,还会影响机能,由于每次要求中 Cookie 都邑作为 http 头被一同发送.

save 要领用 getValue 要领取得所要保留的对象值,getValue() 要领各个子类中的完成各不雷同.运用 save 要领,没必要提交表单也能保留表单的内容.
这个关于长表单来讲很有效,由于用户可以没必要填完表单半途保留, 然后忙完其他事变再来完成表单的填写:

var InputField = function(id, label) { // implements Composite, FormItem
    Field.call(this, id);

    this.input = document.createElement('input');
    this.input.id = id;

    this.label = document.createElement('label');
    var labelTextNode = document.createTextNode(label);
    this.label.appendChild(lavelTExtNode);


    this.element = document.createElement('div');
    this.element.className = 'input-field';
    this.label.appendChild(this.label);
    this.label.appendChild(this.input);
};
extend(InputField, Field); // Inherit from Field.
InputField.prototype.getValue = function () {
    return this.input.value;
};

InputField 是 Field 的子类之一.大多数要领都是从Field 继续而来.然则他也完成了针对 input 标签的 getValue 要领的代码.TextareaField 和 SelectField 也完成了本身特有的 getValue 要领.

var TextareaField = function(id, label) { // implements Composite, FormItem
    Field.call(this, id);

    this.textarea = document.createElement('textarea');
    this.textarea.id = id;

    this.label = document.createElement('label');
    var labelTextNode = document.createElement('select');
    this.select.id = id;

    this.element = document.createElement('div');
    this.element.className = 'input-field';
    this.element.appendChild(this.label);
    this.element.appendChild(this.select);
};
extend(SelectField, Field); // Inherit from Field.

SelectField.prototype.getValue = function () {
    return this.select.options[this.select.selectedIndex].value;
};

运用组合形式,简朴的操纵也能发生庞杂的效果.没必要编写大批手工遍历数据或许其他数据构造的粘合代码,只需对最顶层的对象实行操纵,让每一个子对象本身通报这个操纵.
组合形式中,各个对象之间的耦合异常松懈.只需他们完成了一样的接口,那末转变他们的位置或许相互交流很简朴.促进了代码得宠用,也有利于代码重构.
用组合形式构造起来的对象形成了一个精彩的条理系统.每当对顶层对象实行一个操纵时,现实上是在对全部构造举行深度优先搜刮以查找节点.在该条理系统中增加,删除,查找节点都很轻易.

组合对象的易用性可以掩盖了它所支撑的每一种操纵的价值.由于对组合对象挪用的任何操纵都邑被通报到他的一切子对象,那末假如条理系统很大,系统的机能就会受到影响.topGallery.show()如许一个要领的挪用会引起一次对全部数构造的遍历.

小结

它将一批子对象构造为树形构造,只需一条敕令就可以操纵树中的一切对象.他进步了代码的模块化水平,而且便于代码重构和对象的替代.这类形式迥殊实用于动态的 HTML 用户界面,在他的协助下,可以在不知道用户界面的终究款式的情况下举行开辟.

    原文作者:南赐
    原文地址: https://segmentfault.com/a/1190000006995637
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞