Javascript 接口;设计模式的经典语录“针对接口而不是类编程”
Javascript是种弱类型语言,类型不匹配错误很难跟踪,使用接口可以让这种错误的查找变得更容易点;接口还可以让逻辑代码变得更稳固,因为接口添加一个操作,类必须实现它。
Javascript接口缺点:在某种程序上增加额外方法调用的开销(可以在开发完成后剔除接口代码);Javascript接口必须用手工的办法保证某个类实现某个接口,编码规范和辅助类可以提供一些帮助,但无法彻底根除这个问题,除非项目组的人员都同意并且强制使用接口并对其检查,否则接口很多价值都无法体现。
1、 在Javascript中模仿接口。使用注释来模仿其它语言的接口做法。代码如下:
/*
interface Composite{
function add(child);
function remove(child);
function getChild(index);
}
interface FormItem{
function save();
}
*/
上面的一段注释即为模仿接口,只是人为按照这种方式来实现这种接口。程序并不会真正继承,程序也不会检查错误。实现接口代码如下:
var CompositeForm=function(id,method,action){ //implements Composite,FormItem
…
};
//实现Composite接口
CompositeForm.prototype.add=function(child){
…
};
CompositeForm.prototype.remove=function(child){
…
};
CompositeForm.prototype.getChild=function(index){
…
};
//实现FormItem接口
CompositeForm.prototype.save=function(){
…
};
尽管以上如此,只是人为的模仿,但也有其优点。不需要额外的类或函数。可以提高代码的可重用性,因为现在那些类实现的接口都有说明。缺点就是不会提供错误消息,对测试和调试没有帮助。
2、 用属性检查模仿接口。
这一种方法要更严谨一点。但仍然只是注释,但现在可以通过检查一个属性得知某个类自称实现了什么接口。代码如下:
/*
interface Composite{
function add(child);
function remove(child);
function getChild(index);
}
interface FormItem{
function save();
}
*/
var CompositeForm=function(id,method,action){ //implements Composite,FormItem
this.implementsInterfaces=[‘Composite’,’FormItem’];
…
};
//实现Composite接口
CompositeForm.prototype.add=function(child){
…
};
CompositeForm.prototype.remove=function(child){
…
};
CompositeForm.prototype.getChild=function(index){
…
};
//实现FormItem接口
CompositeForm.prototype.save=function(){
…
};
function addForm(formInstance){
If(!implements(formInstance,’Composite’,’FormItem’)){
Throw new Error(“Object does not implement a require interface”);
}
}
function implements(object){
for(var i=1;i<arguments.length;i++){
var interfaceName=arguments[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;
}
以上这种方法只是模仿接口的改进。可以检查是否实现某个接口,但并不真正的实现。
3、 用鸭式辨型模仿接口。
其实,类是否声明自己支持那些接口并不重要,只要它具有这些接口中的方法就行。把对象实现的方法集作为判断它是不是某个类的实例的唯一标准。这种技术在检查一个类是否实现了某个接口时也大显身手。这就是鸭式辨型模仿接口。可以用一个辅助函数来确保对象具有所有必需的方法。代码如下:
//接口.
var Composite=new Interface(‘Composite’,[‘add’,’remove’,’getChild’]);
var FormItem=new Interface(‘FormItem’,[‘save’]);
//实现类
var CompositeForm=function(id,method,action){ …
};
……
Function addForm(formInstance){
ensureImplements(formInstance,Composite,FormItem);
//如果没有实现某个方法这个函数将会抛出一个错误
}
ensureImplements函数至少需要两个参数,每一个是想要检查的对象,其余参数是据以对那个对象进行检查的接口。这种方法需要一个辅助类Interface和一个辅助函数ensureImplements。而且它只关心方法的名称,并不检查其参数的名称、数目或类型。些方式是上述三种方法中最有用的一种。
在现实中,我们可以综合使用第一和第三种方法。即可以提高代码的可重用性及其文档的完善,又可以用辅助类和方法来对对象实现的方法进行显式检查。
Interface 定义代码如下:
var Interface = function (name, methods) {
if (arguments.length != 2) {
throw new Error("the interface length is bigger than 2");
}
this.Name = name;
this.Method = [];
for (var i = 0; i < methods.length; i++) {
if(typeof methods[i]!== string) {
throw new Error("the method name is not string");
}
this.Method.push(methods[i]);
}
}
/* ensureImplement 方法实现*/
Interface.ensureImplement = function (object) {
if (arguments.length < 2) {
throw new Error("there is not Interface or the instance");
}
for (var i = 1; i < arguments.length; i++) {
var interface1 = arguments[i];
if (interface1.constructor !== Interface) {
throw new Error("the argument is not interface");
}
for (var j = 0; j < interface1.Method.length; j++) {
var method = interface1.Method[j];
if (!object[method] || typeof object[method] !== function) {
throw new Error("you instance doesnt implement the interface");
}
}
}
}
使用示例:
var DynamicMap=new Interface(‘DynamicMap’,[‘centerOnPoint’,’zoom’,’draw’]);
function displayRoute(mapInstance){
Interface.ensureImplements(mapInstance,DynamicMap);
mapInstance.centerOnPoint(12,34);
mapInstance.zoom(5);
mapInstance.draw();
…
}
下面这段代码简要的说明了其使用:
var Person = new Interface("Person", ["GetName", "GetAge"]);
var Man = function (name, age) {
this.Name = name;
this.Age = age;
}
Man.prototype = {
GetName: function () { return this.Name; }, //
GetAge: function () { return this.Age; }
}
var test = function (instance) {
Interface.ensureImplement(instance, Person);
var name = instance.GetName();
alert(name);
}
test(new Man("Alan",20));
如果我们注释了上面的GetAge方法,在执行的时候就会出错。在ensureImplement的时候发现并没有去实现这个方法。
4、 设计模式中依赖于接口的主要有以下这些。
工厂模式
组合模式
装饰者模式
命令模式
会在以后的章节中介绍。
完成于2013-01-17 20:54