将抽象与实现隔离开来,以便二者独立变化。常用于事件驱动的程序设计。有些地方它和适配器与门面很类似。下面是最简单的桥接模式,但适配器是一种包装器。门面一般是同一个类中方法的抽象和组合,而桥接通常是收集几个类的。
这种方式也要注意使用场合,如果一个函数只在一个地方专用,那就没必要用Bridge把它桥接出来。有些设计模式和队列混合使用会起到意想不到的效果。
addEvent(element, 'click', getBeerByIdBridge);
function getBeerByIdBridge (e) {
//这里把参数分开后,可以随便调用多个具体实现类,而JS又与参数无关
getBeerById(this.id, function(beer) {
console.log('Requested Beer: '+beer);
});
}
目的:通过将实现和抽象放在两个不同的类层次中而使它们可以独立改变
原则 ✴将实现解耦,让它和界面之间不再永久绑定 ✴抽象和实现可以独立扩展,不会影响到对方 ✴适用于使用在需要跨多个平台上的图形和窗口系统上 ✴缺点是增加了系统的复杂度。
可用来在现有接口和不兼容的类之间进行适配,又称为「包装器」。适配器可以被添加到现有代码中以协调两个不同的接口。适配器有利于大批量改写现有代码,但有时改写现有代码可能会比较方便。这种优化有点类似于JDK升级过程中保留老的API,但建议使用新的API的过程。
门面元素展现的是一个简化接口,它并不提供额外的选择,而且有时为了方便完成某些常见任务它还会做出一些选择和假定。而适配器则要把一个接口转换为另一个接口,它并不会滤除某些能力,也不会简化接口。最简单的例子如下:
var clientObject = {
string1: 'foo',
string2: 'bar',
string3: 'baz'
};
function interfaceMethod(str1, str2, str3) {
//doSomeThing
}
//适配器,为了适配interfaceMethod接口
function clientToInterfaceAdapter(o) {
interfaceMethod(o.string1, o.string2, o.string3);
}
/* Usage. */
clientToInterfaceAdapter(clientObject);
// Prototype $ function.
function $() {
var elements = new Array();
return elements;
}
/* YUI get method. */
YAHOO.util.Dom.get = function(el) {
return el;
};
function PrototypeToYUIAdapter() {
return YAHOO.util.Dom.get(arguments);
}
function YUIToPrototypeAdapter(el) {
return $.apply(window, el);
}
//使用时,从Prototype切换到YUI,需要添加如下代码,这样就不需要修改原有代码了,只需要把原来提供的API重新封装一次即可
$ = PrototypeToYUIAdapter;
//使用时,从YUI切换到Prototype,需要添加如下代码
YAHOO.util.Dom.get = YUIToPrototypeAdapter;
代理是一个对象,它可以用来控制对另一对象的访问。它与另一个对象实现同样的接口,并且会把任何方法调用传递给那个对象。另外那个对象通常称为本体。代理可以代替其本体被实例化,并使其可被远程访问。它还可以把本体的实例化推迟到真正需要的时候。
对于实例化比较费时的本体,或尺寸比较大以至于不用时不宜保存在内存中的本体这特别有用。另外在处理那些需要较长时间才能把数据载入用户界面的类时,代理也非常有用。代理最适合的场景就是创建一个开销昂贵的资源访问。
这个例子没有什么实际的作用,可以做为优化的预留接口。
在用到时再实例化本体,其网页加载时可能没办法一步初始化PublicLibrary。在第一次调用时才会实例化,这可能会导致第一次调用时时间会比较慢。
var PublicLibraryVirtualProxy = function(catalog) { // implements Library
this.library = null;
this.catalog = catalog; // Store the argument to the constructor.
};
PublicLibraryVirtualProxy.prototype = {
_initializeLibrary: function() {
if(this.library === null) {
this.library = new PublicLibrary(this.catalog);
}
},
findBooks: function(searchString) {
this._initializeLibrary();
return this.library.findBooks(searchString);
}
};
一般用来处理加载数据量或处理比较慢的程序,可以在加载前显示正在处理等字样。它们的缺点就是掩盖了本体的大量细节,而且可以直接和本体互换。所以最好是高质量的文档化。
var DynamicProxy = function() {
this.args = arguments;
this.initialized = false;
if(typeof this.class != 'function') {
throw new Error('DynamicProxy: the class attribute must be set before ' +
'calling the super-class constructor.');
}
// Create the methods needed to implement the same interface.
for(var key in this.class.prototype) {
// Ensure that the property is a function.
if(typeof this.class.prototype[key] !== 'function') {
continue;
}
// Add the method.
var that = this;
(function(methodName) {
that[methodName] = function() {
if(!that.initialized) {
return
}
return that.subject[methodName].apply(that.subject, arguments);
};
})(key);
}
};
DynamicProxy.prototype = {
_initialize: function() {
this.subject = {}; //触发本体的实例化过程.
this.class.apply(this.subject, this.args);
this.subject.__proto__ = this.class.prototype;
var that = this;//每隔一段时间触发一次,一旦实例化完成,则会阻止本体的一切方法调用
this.interval = setInterval(function() { that._checkInitialization(); }, 100);
},
_checkInitialization: function() {
if(this._isInitialized()) {
clearInterval(this.interval);
this.initialized = true;
}
},
_isInitialized: function() { // Must be implemented in the subclass.
throw new Error('Unsupported operation on an abstract class.');
}
};
var TestProxy = function() {
this.class = TestClass;
var that = this;
addEvent($('test-link'), 'click', function() { that._initialize(); });
// Initialization trigger.
TestProxy.superclass.constructor.apply(this, arguments);
};
extend(TestProxy, DynamicProxy);
TestProxy.prototype._isInitialized = function() {
... // Initialization condition goes here.
};