学习JavaScript设计模式(接口)
前端之家收集整理的这篇文章主要介绍了
学习JavaScript设计模式(接口),
前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
1、接口概述
1)、什么是接口?
接口是提供了一种用以说明一个对象应该具有哪些方法的手段。尽管它可以表明这些方法的语义,但它并不规定这些方法应该如何实现。
接口可以告诉程序员一个类实现了哪些方法,从而帮助其使用这个类。
- 有助于稳定不同类之前的通信方式。
- 测试和调式因此也能变得更轻松。
在javascript这种弱类型语言中,类型不匹配错误很难跟踪。使用接口可以让这种错误的查找变午更容易一点,因为此时如果一个对象不像所要求的类型,或者没有实现必要的方法,那么你会得到包含有用信息的明确的错误提示。这样一来,逻辑错误可以被限制在方法自身,而不是在对象构成之中。
因为对接口的任何改变在所有实现它的类都必须体现出来。如果接口添加了一个操作,而某个实现它的类并没有相应的添加这个操作,那么你肯定会立即见到一个错误。
3)、接口之弊
javascript是一种具有极强表现图片的语言,这主要得益于其弱类型的特点。而接口的使用则一定程序上强化了类型的作用。这降低了语言的灵活性。javascript并没有提供对接口的内置支持,而试图模仿其它语言内置的功能总会有一些风险。
js中接口使用的最大问题在于,无法强迫其他程序员遵守你定义的接口。在其它语言中,接口的概念是内置的,如果某人定义了实现一个接口的类,那么编译器会确保该类的确实现了这个接口。而在javascript中则必须用手工的办法保证某个类实现了一个接口。编码规范和辅助类可以提供一些帮助,但无法彻底根除这个问题。如果项目的其他程序员不认真对待接口,那么这些接口的使用是无法得到强制性保证的。除非项目的所有人都同意使用接口并对其进行检查,否则接口的很多价值都无从体现。
2、在javascript中模仿接口
javascript中模仿接口的三种方法:注解描述法、属性检查法、鸭式辨型法。
没有哪种技术是完美的,但三者结合使用基本上可以令人满意。
用注释模仿接口是最简单的方法,但效果却是最差的。这种方法模仿其他页面对象语言中的做法,使用了interface和implements关键字,但把它们放在注释中,以免引起语法错误。如下:
<div class="jb51code">
<pre class="brush:js;">
//javascript中定义接口的方式有三种:
//1、注解描述的方式
/**
- interface Composite{
- function add(obj);
- function remove(obj);
- function update(obj);
}
优点:程序员可以有参考
缺点:缺点一大堆,他只是一个借口的文档范畴,假如不实现
所有的方法,程序照样可以运行,太松散了。对测试和调试难度大
*/
// Implement of interface Composite
var CompositeImpl =function(){
/*this.add = function(obj){
};
this.remove = function(obj){
};
这种函数定义的方法,在实例化一个对象的时候,new
一个示例,将产生一个方法,且各个实力的方法还不一样。
所以采用下面的方法:
*/
CompositeImpl.prototype.add = function(obj){
}
CompositeImpl.prototype.remove = function(obj){
}
CompositeImpl.prototype.update = function(obj){
}
}
var c1 = new CompositeImpl();
var c2 = new CompositeImpl()
alert(c1.add == c2.add)
这种模仿并不是很好。它没有为确保Composite真正实现了正确的方法集而进行检查,也不会抛出错误以告知程序员程序中的问题。说到底它主要还是属于程序文档范畴。在这种做法中,对接口约定的遵守完全依靠自觉。
2)、属性检测法实现接口
这种方法更严谨一点。所有类都明确地声明自己实现了哪些接口,那些想与这些类打交道的对象可能针对这些声明进行检查。那些接口自身仍然只是注释,但现在你可以通过检查一个属性得知某个类自称实现了什么接口。
<div class="jb51code">
<pre class="brush:js;">
/**
-
interface Composite{
-
function add(obj);
-
function remove(obj);
-
function update(obj);
-
}
-
interface FormItem{
-
function select(obj);
-
}
*/
// CompositeImpl implements interface Composite,FormItem
var CompositeImpl =function(){
//显示在类的内部,接收所实现的接口,一般来说,这是一个规范,
// 我们项目经理:在内部类定义一个数组,名字要固定
this.interfaceImplments = ['Composite','FormItem'];
CompositeImpl.prototype.add = function(obj){
alert("小平果");
}
CompositeImpl.prototype.remove = function(obj){
}
CompositeImpl.prototype.update = function(obj){
}
/*CompositeImpl.prototype.select = function(obj){
}*/
}
//定义函数检测,判断当前对象是否实现了所有的接口
function checkCompositeImpl (instance){
if (!isImplments(instance,'Composite','FormItem')) {
throw new Error('Object cannot implements all the interface');
};
}
//公用的具体检测方法(核心方法),主要目的就是判断示例对象有没有实现相关的接口;
function isImplments(object){
//arguments 对象会的函数的实际对象
for (var i = 1,len = arguments.length; i < len; i++) { //注意这里从1开始,逐个方法判断。
var interfaceName = arguments[i]; //接收实现每一个接口的名字
var interfaceFound = false;//判断此方法到底是实现了还是失败了?规范里定义了interfaceImplments.
for (var j = 0;j < object.interfaceImplments.length; j++) {
if(object.interfaceImplments[j] == interfaceName){
interfaceFound = true;
break;
}
};
//如果没有实现,则返回false
if (!interfaceFound) {
return false;
};
}
return true;
}
var c1 = new CompositeImpl();
checkCompositeImpl(c1);
c1.add();
这个例子中,CompositeImpl 宣称自己实现了Composite接口,其做法是把这两个接口名称加入一个名为implementsInterfaces的数组。类显式声明自己支持什么接口。任何一个要求基于参数属于特定类型的函数都可以对这个属性进行检查,并在所需接口未在声明之列时抛出一个错误。
这种方法有几个优点。它对类所实现的接口提供了文档说明。如果需要的接口不在一个类宣称支持的接口之列,你会看到错误消息。通过利用这些错误,你可以强迫其他程序员声明这些接口。
这种方法的主要缺点在于它并未确保类真正实现了自称实现的接口。你只知道它是否说自己实现了接口。在创建一个类时声明它实现了一个接口,但后来在实现该接口所规定的方法时却漏掉其中的某一个,这种错误很常见。此时所有检查都能通过,但那个方法却不存在,这将在代码中埋下一个隐患。另外显式声明类所支持的接口也需要一些额外的工作。
3)、鸭式辨型法实现接口
其实,类是否声明自己支持哪些接口并不重要,只要它具有这些接口中的方法就行。鸭式辨型(这个名称来自James Whitomb Riley的名言:“像鸭子一样走路并且嘎嘎叫的就是鸭子”)正是基于这样的认识。它把对象实现的方法集作作为判断它是不是某个类的实例的唯一标准。这种技术在检查一个类是否实现了某个接口时也可大显向身手。这种方法背后的观点很简单:如果对象具有与接口定义的方法同名的所有方法,那么就可以认为它实现了这个接口。你可以用一个辅助函数来确保对象具有所有必需的方法:
<div class="jb51code">
<pre class="brush:js;">
/ 实现接口的第三种方式:鸭式辨型发实现接口,(较为完美的实现方法)
核心思想:一个类实现接口的主要目的:把其中的方法都实现了(检测方法)
完全面向对象 代码实现统一,实现解耦/
//1、接口类---Class Interface ===>实例化N多个接口
/*
接口类的参数?几个
- 参数1:接口名
- 参数2:接收方法的集合(数组)
*/
var Interface = function(name,methods){
//判断接口的参数个数
if (arguments.length !=2) {
throw new Error('the instance interface constructor arguments should be 2');
};
this.name =name;
//this.methods = methods;
this.methods = [];
for (var i = 0,len = methods.length; i <len; i++) {
if (typeof methods[i] !== "string"){
throw new Error('the name of method is wrong');
}
this.methods.push(methods[i]);
}
}
//2、准备工作,具体的实现
//(1)实例化接口对象
var CompositeInterface = new Interface('CompositeInterface',['add','delete']);
var FormItemInterface = new Interface('FormItemInterface',['update','select']);
//(2)具体的实现类
//CompositeImpl implments CompositionIterface FormItemIterface
var CompositeImpl = function(){
}
//(3)实现接口的方法 implements methods
CompositeImpl.prototype.add = function(obj){
alert("add");
}
CompositeImpl.prototype.delete = function(obj){
alert("delete");
}
CompositeImpl.prototype.update = function(obj){
alert("update");
}
/CompositeImpl.prototype.select = function(obj){
alert("select");
}/
//3、检验接口里的方法
//如果检测通过,不做任何操作;不通过,则抛出异常。
//这个方法的目的就是 检测方法的
Interface.ensureImplements =function(object){
//如果接受参数长度小于2 ,证明还有任何实现的接口
if (arguments.length < 2) {
throw new Error('The Interface has no implement class');
};
//获得接口的实例对象
for (var i = 1,len= arguments.length; i < len; i++) {
var instanceInterface =arguments[i];
//判断参数是否为 接口类的类型
if (instanceInterface.constructor !==Interface) {
throw new Error('The arguments constructor is not Interface Class');
};
for (var j = 0,len2 =instanceInterface.methods.length ; j <len2; j++ ) {
//用一个临时变量 ,接收每个<a href="https://www.jb51.cc/tag/fangfa/" target="_blank" class="keywords">方法</a>的名字(注意为字符串类型)
var methodName = instanceInterface.methods[j];
//object[key] 获得<a href="https://www.jb51.cc/tag/fangfa/" target="_blank" class="keywords">方法</a>
if (!object[methodName] || typeof object[methodName] !== 'function')
{
throw new Error('the method"'+ methodName+'"is not found');
}
}
}
}
var c1 =new CompositeImpl();
Interface.ensureImplements(c1,CompositeInterface,FormItemInterface);
c1.add();
严格的类型检查并不总是明智的。许多js程序员根本不用接口或它所提供的那种检查,也照样一干多年。接口在运用设计模式实现复杂系统的时候最能体现其价值。它看似降低javascript的灵活性,而实际上,因为使用接口可以降低对象间的耦合程度,所以它提高了代码的灵活性。接口可以让函数变得更灵活,因为你既能向函数传递任何类型的参数,又能保证它只会使用那些具有必要方法的对象。