读书笔记之 - javascript 设计模式 - 接口、封装和链式调用

javascript 采用设计模式主要有下面的三方面原因:

  1. 可维护性:设计模式有助于降低模块之间的耦合程度。这使代码进行重构和换用不同的模块变得容易,也使程序员在大型项目中合作变得容易。
  2. 沟通:设计模式为处理不同类型的对象提供了一套通用的术语。程序员可以简洁的描述自己系统的工作方式。
  3. 性能:采用一些优化性能的模式,可以大幅度提高程序的执行效率,如享元模式和代理模式等

同时,滥用设计模式也会带来一些后果:

  1. 复杂性:代码变得复杂,新手难以理解
  2. 性能:多数设计模式会或多或少的降低代码的性能

实现容易,合理使用才是难点。一个建议就是:尽量选用最合适的那种,但不要过度的牺牲性能。

接口:接口是用来说明该对象具有哪些方法的手段,尽管其表明这些方法的语义,但是它不规定实现。

在Javascript中模仿接口

一:用注释描述接口  

/*
 interface Composite{
 function add(child);
 function remove(child);
 function getChild(index);
 }
 interface FormItem{
 function save();
 }
 */

var CompositeForm = function(id,method,action){

}
CompositeForm.prototype.add = function(){

}
CompositeForm.prototype.remove = function(){

}
CompositeForm.prototype.getChild = function(){

}
CompositeForm.prototype.save = function(){

}

这种模仿只是停留在文档阶段。没有对是否实现正确的方法进行检查,也不会抛出错误,完全依靠自觉。

二:用属性检查模仿接口:

/*
 interface Composite{
 function add(child);
 function remove(child);
 function getChild(index);
 }
 interface FormItem{
 function save();
 }
 */

var CompositeForm = function(id,method,action){
    this.implementsInterfaces = [‘Composite‘,‘FormItem‘];

}

function addForm(formInstance){
    if(!implements(formInstance,‘Composite‘,‘FormItem‘){
        throw new Error("Object does not implement a required interface.");
    })
}

function implements(object){
    for(var i=1;i<arguments.length;i++){
        var interfaceName = argument[i];
        var interfaceFound = false;
        for(var j=0;j<object.implementsInterfaces.length;j++){
            if(object.implementsInterfaces[j]==interfaceName){
                interfaceFound = true;
                break;
            }
        }
        //有接口没有找到
        if(!interfaceFound){
            return false;
        }
    }
    //找到了所有的接口
    return true;
}

这里,CompositeForm 宣称自己实现了‘Composite‘,‘FormItem‘这俩个接口,其做法是把这俩个接口的名称加入到一个对象的数组中,显式的声明自己支持的接口。

任何一个要求其参数属于特定类型的函数都可以对这个属性进行检查,并在找不到实现方法的时候,抛出异常。

三、鸭式辨型模仿接口

它把对象实现的方法集作为判断它是不是某个类的唯一标准,这种方法背后的观点很简单:如果对象具有与接口定义的方法同名的所有方法,那么就可以认为它实现了这个接口。

Interface 类的定义

/*
 * 定义接口方法
 */
var Interface = function(name,methods){
    if(arguments.length!=2){
        throw new Error("Interface constructor called with " + arguments.length +
            "arguments, but expected exactly 2.");
    };

    this.name = name ;
    this.methods = [];

    for (var i = 0,len = methods.length - 1; i < len; i++) {
        if(typeof methods[i]!==‘string‘){
            throw new Error("Interface constructor expects method names to be "
                + "passed in as a string.");
        }
        this.methods.push(methods[i]);
    };
};

/*
 * 给Interface扩展静态验证方法
 */

Interface.ensureImplements = function(obj){
    if(arguments.length<2){
        throw new Error("Function Interface.ensureImplements called with " +
            arguments.length  + "arguments, but expected at least 2.");
    };

    for (var i = 0,len = methods.length - 1; i < len; i++) {
        var interface = arguments[i];
        if(interface.constructor!==interface){
            throw new Error("Function Interface.ensureImplements expects arguments "
                + "two and above to be instances of Interface.");
        };

        for (var j = 0,methodsLen = interface.methods.length; j<methodsLen; j++) {
            var method = interface.methods[j];
            if(!object[method]||typeof object[method] !== ‘function‘){
                throw new Error("Function Interface.ensureImplements: object "
                    + "does not implement the " + interface.name
                    + " interface. Method " + method + " was not found.");
            };

        };
    };
}

继承

链式继承的实现

function extend(subClass,superClass){
    var F = function(){};
    F.prototype = superClass.prototype;
    subClass.prototype = new F();
    subClass.peototype.constructor = subClass;

    subClass.superclass = superClass.prototype;
    if(superClass.prototype.constructor == Object.prototype.constructor){
        superClass.prototype.constructor = superClass;
    }
}

superclass 用来弱化子类和超类之间的耦合。同时确保超类的constructor被正确设置。

原型继承的实现,实际上是拷贝继承,实现方式如下。

function clone(object){
    function F(){};
    F.prototype = object;
    return new F;
}

实际上返回的是一个以给定对象为原生对象的空对象。

掺元类的实现。有一种重用的方法不需要用到严格的继承,如果想把一个函数用到多个类中,可以扩充的方式让这些类共享该函数,做法:先创建一个包含各种通用方法的类,然后再用它扩充其他的类,这种包含通用方法的类称为掺元类。

掺元类的实现方法:

function augment(receivingClass,givingClass){
    //if has the third arg
    if(arguments.length[2]){
        for(var i=2,len = arguments.length;i<len;i++){
            receivingClass.prototype[arguments[i]] = givingClass.prototype[arguments[i]];
        }
    }else{
        for(methodName in givingClass.prototype){
            if(!receivingClass.prototype[methodName]){
                receivingClass.prototype[methodName] = givingClass.prototype[methodName];
            }
        }
    }
}

封装

封装就是对对象内部的数据表现形式和实现细节进行隐藏。如果想访问封装过的对象,只能使用已定义的操作这一种方式。

在javascript中,我们没有关键字,只能使用闭包的概念来创建私有的属性和方法。

javascript 想创建对象的基本模式有三种。

门户大开式,用下划线表示私有方法和属性,用闭包来创建真正的私有成员。

var Book = function(newIsbn,newTitle,newAuthor){

    //私有属性
    var isbn,title,anthor;
    function checkIsbn(isbn){
    ...
    };

    //特权方法,可以访问私有变量
    this.getIsbn = function(){
        return isbn;
    };
    this.setIsbn = function(){
        if(!checkIsbn(newIsbn)) throw new Error(‘Book:Invalid ISBN.‘);
        isbn = newIsbn;
    };
    this.getTitle = function(){
        return newTitle;
    };
    this.setTitle = function(newTitle){
        title = newTiyle||‘‘;
    };
    this.getAuthor = function(){
        return author;
    };
    this.setAuthor = function(newAuthor){
        author = newAuthor||‘‘;
    };

    this.setIsbn(newIsbn);
    this.setTitle(newTitle);
    this.setAuthor(newAuthor);
}
//不需要直接访问私有属性的方法都可以在prototype中声明。
Book.prototype = {
    display:function(){...}
}

更高级的对象创建。

var Book = (function(){
    //私有静态属性
    var numOfBooks = 0;
    //私有静态方法
    function checkIsbn(isbn){
    ...
    };

    return function(newIsbn,newTitle,newAuthor){
        //私有属性
        var isbn,title,anthor;

        //特权方法,可以访问私有变量
        this.getIsbn = function(){
            return isbn;
        };
        this.setIsbn = function(){
            if(!checkIsbn(newIsbn)) throw new Error(‘Book:Invalid ISBN.‘);
            isbn = newIsbn;
        };
        this.getTitle = function(){
            return newTitle;
        };
        this.setTitle = function(newTitle){
            title = newTiyle||‘‘;
        };
        this.getAuthor = function(){
            return author;
        };
        this.setAuthor = function(newAuthor){
            author = newAuthor||‘‘;
        };

        numOfBooks++;
        if(numOfBooks > 50) throw new Err(‘Book:only 50 instances of Book can be created.‘);
        this.setIsbn(newIsbn);
        this.setTitle(newTitle);
        this.setAuthor(newAuthor);
    }

})();
//不需要直接访问私有属性的方法都可以在prototype中声明。
Book.prototype = {
    display:function(){...}
}

一些简单的说明:

  • 首先,用()()创建了一个闭包,立即执行,并返回另一个函数。
  • 其次,返回函数被赋值给Book,成为一个构造函数。
  • 三次,实例化Book调用的内层函数,外层只是用于创建一个可以存放静态私有成员的闭包。
  • 四次,checkIsbn被设计为静态方法,因此Book的每个实例都不会生成该静态方法,该方法作用在构造类上,而不是实例上。
  • 五次,这些静态方法因为包含在闭包中,所以闭包中的其它方法可以访问,而且内存中只存在一份。
  • 六次,这些方法声明在返回构造器之外,不是特权方法,所以不能访问定义在构造器中的私有属性。
  • 七次,判断一个私有方法是否应该被设计为静态方法,一条经验法则则是看它是否要访问任何实例数据,如果不需要,那么将其设计为静态方法则更有效率。
时间: 2024-08-10 23:30:57

读书笔记之 - javascript 设计模式 - 接口、封装和链式调用的相关文章

javascript设计模式 第6章 链式调用

链式调用是一种语法招数.作用:能让你通过重用一个初始化操作来达到用少量代码表达复杂操作的目的.. 这种技术包含两个部分: 1.一个创建代表html元素的对象的工厂.以及一批对这个html元素执行某些操作的方法. 通过例子对比:之前和之后的代码,对链式调用的概念的初步认识.  之前:   addevent($('example'),"click",function(){    setstyle(this,"color",'green');    show(this);

如何写 JS 的链式调用 ---》JS 设计模式《----方法的链式调用

1.以$ 函数为例.通常返回一个HTML元素或一个元素集合. 代码如下: function $(){ var elements = []; for(var i=0;i<arguments.length;i++){ var element = argument[i]; if(typeOf element == "String") { element = document.getElementById(element); } if ( arguments.length === 1)

【读书笔记】大话设计模式 1【2014-7-28】

大 大话设计模式这本书给我最大的感觉就是这本书不是为了说明设计模式而写的,它是为了让你能够更好地看懂,更好地理解而存在.到现在读了也有一半了,稍微总结一下. 0.uml类图. 软件工程的基础就是uml类图.依赖:元素A的变化会影响元素B,但反之不成立,那么B和A的关系是依赖关系,B依赖A:用带箭头的虚线表示,箭头指向被依赖元素.泛化:通常所说的继承(特殊个体 is kind of 一般个体)关系,不必多解释了.uml中用带空心箭头的实线线表示,箭头指向一般个体.实现:这个关系最常用于接口.uml

【读书笔记】大话设计模式 2【2014-7-31】

这本书慢慢的诚意让我没有任何理由不把它阅读完.学校有软件工程这门选修课,但是我感觉看了这本书比选那门课要好得多.里面的故事虽然没有跌宕起伏,但是生活中淡淡的趣味让我读完之后仍然有一种想看续集的冲动.希望学到的这些在我以后的道路上扮演重要的角色! 按照最后一章模式总结的顺序来总结一下: 1.创建型模式 抽象工厂 建造者模式(和工厂的区别:一个在于产生,一个在于组装)(和组合更是两码事了,组合是树形的,不要望文生义~) 工厂方法 原型模式 单例模式(只允许一个stastic存在,把构造方法封死,然后

javascript设计模式实践之职责链--具有百叶窗切换图片效果的JQuery插件(三)

在上一篇<javascript设计模式实践之模板方法--具有百叶窗切换图片效果的JQuery插件(二)>里,通过采用模板方法模式完成了切换效果对象的构建编写. 接下来就是完成各效果对象的调用和联动. 切换要求:当前图片显示指定时间后执行切换效果并切换下一张图片,最后一个切换后从头开始. 按照要求一个效果对象完成后要执行下一个,最后一个完成后要从头开始, 看上去就是一个带状态通知的环形列表,类似于这样的特征,可以采用职责链模式来构建. 职责链就是由不同的功能处理构成的链表,每一个功能处理作为一个

JavaScript设计模式-8.链式调用

1 <!DOCTYPE html> 2 <html> 3 <head> 4 <meta charset="UTF-8"> 5 <title>javascript高级语法8-链式调用</title> 6 </head> 7 <body> 8 <div id="box"></div> 9 <script type="text/javas

JavaScript设计模式——方法的链式调用

方法的链式调用: 1 (function() { 2 //私有类 3 function _$ (els) { 4 this.elements = []; 5 for(var i = 0, len = els.length; i < len; ++i){ 6 var element = els[i]; 7 if(typeof element === 'string'){ 8 element = document.getElementById(element); 9 } 10 this.elemen

关于JavaScript中的setTimeout()链式调用和setInterval()探索

http://www.cnblogs.com/Wenwang/archive/2012/01/06/2314283.html http://www.cnblogs.com/yangjunhua/archive/2012/04/12/2444106.html 下面的参考:http://evantre.iteye.com/blog/1718777 1.选题缘起 在知乎上瞎逛的时候看到一个自问自答的问题: 知乎上,前端开发领域有哪些值得推荐的问答?,然后在有哪些经典的 Web 前端或者 JavaScr

JavaScript 链式调用

我们使用jquery的时候,jquery的简单的语法及可实现链式调用方法,现在我们自己也封装一个链式调用的方法,来理解下 jquery中如何封装链式调用 无非就是每次调用一个方法的时候 给这个方法返回this即可,this指向该对象自身,我们看看代码: 1 // 定义一个简单的对象,每次调用对象的方法的时候,该方法都返回该对象自身 2 var obj = { 3 a: function(){ 4 console.log("输出a"); 5 return this; 6 }, 7 b:f