JavaScript设计模式深入分析(1)(3)
桥接(bridge)模式:在实现API的时候,桥梁模式灰常有用。在所有模式中,这种模式最容易立即付诸实施。
桥梁模式可以用来弱化它与使用它的类和对象之间的耦合,就是将抽象与其实现隔离开来,以便二者独立变化;这种模式对于JavaScript中常见的时间驱动的编程有很大益处,桥梁模式最常见和实际的应用场合之一是时间监听器回调函数。先分析一个不好的示例:
- element.onclick = function(){
- new setLogFunc();
- };
为什么说这个示例不好,因为从这段代码中无法看出那个LogFunc方法要显示在什么地方,它有什么可配置的选项以及应该怎么去修改它。换一种说法就是,桥梁模式的要诀就是让接口“可桥梁”,实际上也就是可配置。把页面中一个个功能都想象成模块,接口可以使得模块之间的耦合降低。
掌握桥梁模式的正确使用收益的不只是你,还有那些负责维护你代码的人。把抽象于其实现隔离开,可独立地管理软件的各个部分,bug也因此更容易查找。
桥梁模式目的就是让API更加健壮,提高组件的模块化程度,促成更简洁的实现,并提高抽象的灵活性。一个好的示例:
- element.onclick = function(){//API可控制性提高了,使得这个API更加健壮
- new someFunction(element,param,callback);
- }
注:桥梁模式还可以用于连接公开的API代码和私有的实现代码,还可以把多个类连接在一起。在文章封装介绍的部分提到过特权方法,也是桥梁模式的一种特例。《JS设计模式》上找的示例,加深大家对这个模式的理解:
- //错误的方式
- //这个API根据事件监听器回调函数的工作机制,事件对象被作为参数传递给这个函数。本例中并没有使用这个参数,而只是从this对象获取ID。
- addEvent(element,'click',getBeerById);
- function(e){
- var id = this.id;
- asyncRequest('GET','beer.url?id=' + id,function(resp){
- //Callback response
- console.log('Requested Beer: ' + resp.responseText);
- });
- }
- //好的方式
- //从逻辑上分析,把id传给getBeerById函数式合情理的,且回应结果总是通过一个毁掉函数返回。这么理解,我们现在做的是针对接口而不是实现进行编程,用桥梁模式把抽象隔离开来。
- function getBeerById(id,callback){
- asyncRequest('GET','beer.url?id=' + id,function(resp){
- callback(resp.responseText)
- });
- }
- addEvent(element,'click',getBeerByIdBridge);
- function getBeerByIdBridge(e){
- getBeerById(this.id,function(beer){
- console.log('Requested Beer: ' + beer);
- });
- }
装饰者(Decorator)模式:这个模式就是为对象增加功能(或方法)。
动态地给一个对象添加一些额外的职责。就扩展功能而言,它比生成子类方式更为灵活。
装饰者模式和组合模式有很多共同点,它们都与所包装的对象实现统一的接口并且会把任何方法条用传递给这些对象。可是组合模式用于把众多子对象组织为一个整体,而装饰者模式用于在不修改现有对象或从派生子类的前提下为其添加方法。
装饰者的运作过程是透明的,这就是说你可以用它包装其他对象,然后继续按之前使用那么对象的方法来使用,从下面的例子中就可以看出。还是从代码中理解吧:
- //创建一个命名空间为myText.Decorations
- var myText= {};
- myText.Decorations={};
- myText.Core=function(myString){
- this.show = function(){return myString;}
- }
- //第一次装饰
- myText.Decorations.addQuestuibMark =function(myString){
- this.show = function(){return myString.show()+'?';};
- }
- //第二次装饰
- myText.Decorations.makeItalic = function(myString){
- this.show = function(){return '<li>'+myString.show()+'</li>'};
- }
- //得到myText.Core的实例
- var theString = new myText.Core('this is a sample test String');
- alert(theString.show());//output 'this is a sample test String'
- theString = new myText.Decorations.addQuestuibMark(theString);
- alert(theString.show());//output 'this is a sample test String?'
- theString = new myText.Decorations.makeItalic (theString);
- alert(theString.show());//output '<li>this is a sample test String</li>'
从这个示例中可以看出,这一切都可以不用事先知道组件对象的接口,甚至可以动态的实现,在为现有对象增添特性这方面,装饰者模式有极大的灵活性。
如果需要为类增加特性或者方法,而从该类派生子类的解决办法并不实际的话,就应该使用装饰者模式。派生子类之所以会不实际最常见的原因是需要添加的特性或方法的数量要求使用大量子类。
组合(Composite)模式:将对象组合成树形结构以表示“部分-整体”的层次结构。它使得客户对单个对象和复合对象的使用具有一致性。
组合模式是一种专为创建Web上的动态用户界面而量身定制的模式。使用这种模式,可以用一条命令在多个对象上激发复杂的或递归的行为。组合模式擅长于对大批对象进行操作。
组合模式的好处:1.程序员可以用同样的方法处理对象的集合与其中的特定子对象;2.它可以用来把一批子对象组织成树形结构,并且使整棵树都可被便利。
组合模式适用范围:1.存在一批组织成某处层次体系的对象(具体结构可能在开发期间无法知道);2.希望对这批对象或其中的一部分对象实话一个操作。
其实组合模式就是将一系列相似或相近的对象组合在一个大的对象,由这个大对象提供一些常用的接口来对这些小对象进行操作,代码可重用,对外操作简单。例如:对form内的元素,不考虑页面设计的情况下,一般就剩下input了,对于这些input都有name和value的属性,因此可以将这些input元素作为form对象的成员组合起来,form对象提供对外的接口,便可以实现一些简单的操作,比如设置某个input的value,添加/删除某个input等等。
这种模式描述起来比较吃力,我从《JS设计模式》上找个一个实例,大家还是看代码吧:先创建组合对象类
- // DynamicGallery Class
- var DynamicGallery = function (id) { // 实现Composite,GalleryItem组合对象类
- this.children = [];
- this.element = document.createElement('div');
- this.element.id = id;
- this.element.className = 'dynamic-gallery';
- }
- DynamicGallery.prototype = {
- // 实现Composite组合对象接口
- add: function (child) {
- this.children.push(child);
- this.element.appendChild(child.getElement());
- },
- remove: function (child) {
- for (var node, i = 0; node = this.getChild(i); i++) {
- if (node == child) {
- this.children.splice(i, 1);
- break;
- }
- }
- this.element.removeChild(child.getElement());
- },
- getChild: function (i) {
- return this.children[i];
- },
- // 实现DynamicGallery组合对象接口
- hide: function () {
- for (var node, i = 0; node = this.getChild(i); i++) {
- node.hide();
- }
- this.element.style.display = 'none';
- },
- show: function () {
- this.element.style.display = 'block';
- for (var node, i = 0; node = getChild(i); i++) {
- node.show();
- }
- },
- // 帮助方法
- getElement: function () {
- return this.element;
- }
- }
再创建叶对象类
- var GalleryImage = function (src) { // 实现Composite和GalleryItem组合对象中所定义的方法
- this.element = document.createElement('img');
- this.element.className = 'gallery-image';
- this.element.src = src;
- }
- GalleryImage.prototype = {
- // 实现Composite接口
- // 这些是叶结点,所以我们不用实现这些方法,我们只需要定义即可
- add: function () { },
- remove: function () { },
- getChild: function () { },
- // 实现GalleryItem接口
- hide: function () {
- this.element.style.display = 'none';
- },
- show: function () {
- this.element.style.display = '';
- },
- // 帮助方法
- getElement: function () {
- return this.element;
- }
- }
现在我们可以使用这两个类来管理图片:
- var topGallery = new DynamicGallery('top-gallery');
- topGallery.add(new GalleryImage('/img/image-1.jpg'));
- topGallery.add(new GalleryImage('/img/image-2.jpg'));
- topGallery.add(new GalleryImage('/img/image-3.jpg'));
- var vacationPhotos = new DyamicGallery('vacation-photos');
- for(var i = 0, i < 30; i++){
- vacationPhotos.add(new GalleryImage('/img/vac/image-' + i + '.jpg'));
- }
- topGallery.add(vacationPhotos);
- topGallery.show();
- vacationPhotos.hide();
- 上一篇:Node.js源码研究之模块组织加载
- 下一篇:JavaScript原型继承






