读书笔记之 - javascript 设计模式 - 接口、封装和链式调用
javascript 采用设计模式主要有下面的三方面原因:
- 可维护性:设计模式有助于降低模块之间的耦合程度。这使代码进行重构和换用不同的模块变得容易,也使程序员在大型项目中合作变得容易。
- 沟通:设计模式为处理不同类型的对象提供了一套通用的术语。程序员可以简洁的描述自己系统的工作方式。
- 性能:采用一些优化性能的模式,可以大幅度提高程序的执行效率,如享元模式和代理模式等
同时,滥用设计模式也会带来一些后果:
- 复杂性:代码变得复杂,新手难以理解
- 性能:多数设计模式会或多或少的降低代码的性能
实现容易,合理使用才是难点。一个建议就是:尽量选用最合适的那种,但不要过度的牺牲性能。
接口:接口是用来说明该对象具有哪些方法的手段,尽管其表明这些方法的语义,但是它不规定实现。
在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的每个实例都不会生成该静态方法,该方法作用在构造类上,而不是实例上。
- 五次,这些静态方法因为包含在闭包中,所以闭包中的其它方法可以访问,而且内存中只存在一份。
- 六次,这些方法声明在返回构造器之外,不是特权方法,所以不能访问定义在构造器中的私有属性。
- 七次,判断一个私有方法是否应该被设计为静态方法,一条经验法则则是看它是否要访问任何实例数据,如果不需要,那么将其设计为静态方法则更有效率。
读书笔记之 - javascript 设计模式 - 接口、封装和链式调用的更多相关文章
- 读书笔记之 - javascript 设计模式 - 观察者模式
在事件驱动的环境中,比如浏览器这种持续寻求用户关注的环境中,观察者模式是一种管理人与其任务(确切的讲,是对象及其行为和状态之间的关系)之间的关系的得力工具.用javascript的话来讲,这种模式的实 ...
- 如何写 JS 的链式调用 ---》JS 设计模式《----方法的链式调用
1.以$ 函数为例.通常返回一个HTML元素或一个元素集合. 代码如下: function $(){ var elements = []; ;i<arguments.length;i++){ v ...
- javascript学习(10)——[知识储备]链式调用
上次我们简单的说了下单例的用法,这个也是在我们java中比较常见的设计模式. 今天简单说下链式调用,可能有很多人并没有听过链式调用,但是其实只要我简单的说下的话,你肯定基本上都在用,大家熟知的jQue ...
- 读书笔记之 - javascript 设计模式 - 命令模式
本章研究的是一种封装方法调用的方式.命令模式与普通函数有所不同.它可以用来对方法调用进行参数化处理和传送,经过这样处理过的方法调用可以在任何需要的时候执行. 它也可以用来消除调用操作的对象和实现操作的 ...
- 读书笔记之 - javascript 设计模式 - 代理模式
代理(proxy)是一个对象,它可以用来控制对另一对象的访问.它与另外那个对象实现了同样的接口,并且会把任何方法调用传递给那个对象.另外那个对象通常称为本体.代理可以代替本体被实例化,并使其可被远程访 ...
- 读书笔记之 - javascript 设计模式 - 享元模式
本章探讨另一种优化模式-享元模式,它最适合于解决因创建大量类似对象而累及性能的问题.这种模式在javascript中尤其有用,因为复杂的javascript代码很快就会用光浏览器的所有可用内存,通过把 ...
- 读书笔记之 - javascript 设计模式 - 工厂模式
一个类或者对象中,往往会包含别的对象.在创建这种对象的时候,你可能习惯于使用常规方式,即用 new 关键字和类构造函数. 这会导致相关的俩个类之间产生依赖. 工厂模式,就是消除这俩个类之间的依赖性的一 ...
- 读书笔记之 - javascript 设计模式 - 责任链模式
责任链模式可以用来消除请求的发送者和接收者之间的耦合.这是通过实现一个由隐式地对请求进行处理的对象组成的链而做到的.链中的每个对象可以处理请求,也可以将其传给下一个对象. 责任链的结构: 责任链由多个 ...
- 读书笔记之 - javascript 设计模式 - 装饰者模式
本章讨论的是一种为对象增添特性的技术,它并不使用创建新子类这种手段. 装饰者模式可以透明地把对象包装在具有同样接口的另一对象之中,这样一来,你可以给一些方法添加一些行为,然后将方法调用传递给原始对象. ...
随机推荐
- 《University Calculus》-chape6-定积分的应用-求体积
定积分一个广泛的应用就是在求解一些“看似不规则”的几何体的体积,之所以说看似不规则,是因为不规则之下还是有一定的“规则性”可言的,我们就是需要抓住这些线索进行积分运算得到体积. 方法1:切片法. 这里 ...
- 【转】shell 教程——06 Shell变量:Shell变量的定义、删除变量、只读变量、变量类型
Shell支持自定义变量. 定义变量 定义变量时,变量名不加美元符号($),如: variableName="value" 注意,变量名和等号之间不能有空格,这可能和你熟悉的所有编 ...
- javascript排序 查找算法大全
在pptv的实习结束了, 忙着找工作的事,顺便把数据结构的那本书重新复习了一遍.为了加深印象,特意把里面的常用的排序.查找算法用js写了一遍 具体的实例在我的github上,大家可以访问的: http ...
- HDU4283:You Are the One(区间DP)
Problem Description The TV shows such as You Are the One has been very popular. In order to meet the ...
- https_request请求接口返回数据
定义一个https_request方法 <?php function https_request($url, $data = null) { $curl = curl_init(); curl_ ...
- mysql按字段分组并获取每个分组按照某个字段排序的前三条
这是原始数据 想按照brand_id分组 并获取每个分组total_num最高的前3位 SQL语句为: > (select count(*) from data where brand_id = ...
- Java工作队列和线程池
背景 最近的需要做一个与设备通信的web项目.当然,我们需要写好与设备之间的通信协议(socket).大致的时序逻辑时:当用户用浏览器点击页面某一控件后,它就向后台发送一个post请求,后台解析 ...
- Stackdump: 一个可以离线看stackoverflow的工具
博客搬到了fresky.github.io - Dawei XU,请各位看官挪步.最新的一篇是:Stackdump: 一个可以离线看stackoverflow的工具.
- java 图片压缩 剪切 水印 转换 黑白 缩放
专注java已6年,欢迎加入java核心技术QQ群:135138817,每周五晚有群主进行技术讲座. import java.awt.AlphaComposite; import java.awt.C ...
- java获得项目绝对路径
在jsp和class文件中调用的相对路径不同. 在jsp里,根目录是WebRoot 在class文件中,根目录是WebRoot/WEB-INF/classes 当然你也可以用System.getPro ...