JavaScript 面向对象设计---接口

  接口在面向对象编程中是非常重要的概念/工具,早在四人帮(GOF)的Design Pattern 中提到面向对象编程的其中一规则是“面向接口编程,而不要面向实现编程”。--这可见接口在可复用的面向对象设计中的重要性吧~

    对于面向对象JavaScript而言,实现接口并不简单,因为JavaScript并没有内建支持接口。幸运的是,我们可以利用JavaScript的灵活性来模拟实现接口。在模拟接口前,首先了接一下其他面向对象如何定义/处理接口:

    1.Java

1 public interface DataOutput {
2 void writeBoolean(boolean value) throws IOException;
3 void writeByte(int value) throws IOException;
4 void writeChar(int value) throws IOException;
5 void writeShort(int value) throws IOException;
6 void writeInt(int value) throws IOException;
7 ...
8 }

 

 

 DataOutputStream  Class通过关键字implements实现了DataOutput 接口

  1 1 public class DataOutputStream extends FilterOutputStream implements DataOutput {

 2 
 3 2 public final void writeBoolean (boolean value) throws IOException {
 4 
 5 3 write (value ? 1 : 0);
 6 
 7 4 }
 8 
 9 5 ...
10 
11 6 }

 

在接口定义的每一个方法都必须在实现类中实现。如果某一个方法没有被实现,编译的时候会出现一些错误:

DataOutputStream  should be declared abstract; it does not define writeBoolean(boolean) in DataOutputStream  .

 

  2. PHP:

interface MyInterface {
public function interfaceMethod($argumentOne, $argumentTwo);
}
class MyClass implements MyInterface {
public function interfaceMethod($argumentOne, $argumentTwo) {
return $argumentOne . $arguemntTwo;
}
}
class BadClass implements MyInterface {
// No method declarations.
}
// BadClass causes this error at run-time:
// Fatal error: Class BadClass contains 1 abstract methods and must therefore be
// declared abstract (MyInterface::interfaceMethod)

 

 3 C#:

 

interface MyInterface {
string interfaceMethod(string argumentOne, string argumentTwo);
}
class MyClass : MyInterface {
public string interfaceMethod(string argumentOne, string argumentTwo) {
return argumentOne + argumentTwo;
}
}
class BadClass : MyInterface {
// No method declarations.
}
// BadClass causes this error at compile-time:
// BadClass does not implement interface member MyInterface.interfaceMethod()

 

从上面不同的面向对象语言中可以看出,它们定义/处理接口的方式都差不多:

   1.接口定义了需要实现什么方法,这些方法需要什么参数;2.而实现类则显式声明接口中定义的方法并实现之。3.如果实现类没有把接口中所有的方法声明并实现之,则报错。

   当然,在JavaScript的面向对象编程中不能像上面的编程语言来定义,使用接口,因为javaScript中没有interface,implements等关键字,也没有编译的错误检查。

在JavaScript中模拟接口

   我们可以用以下三种方式来在JavaScript模拟接口:

     1.注释法 2.属性检测法 3 特征法

用注释的方法实现接口

/*
interface Composite {
function add(child);
function remove(child);
function getChild(index);
}
interface FormItem {
function save();
}
*/
var CompositeForm = function(id, method, action) { // implements Composite, FormItem
...
};
// Implement the Composite interface.
CompositeForm.prototype.add = function(child) {
...
};
CompositeForm.prototype.remove = function(child) {
...
};
CompositeForm.prototype.getChild = function(index) {
...
};
// Implement the FormItem interface.
CompositeForm.prototype.save = function() {
...
};

  通过上面例子看到,我们用面向对象的方式定义了接口,并把它放置到注释中。这样,既不产生错误,又起了接口定义的作用。不过,这种方法还不能很好地模拟出接口,因为它没有检测机制--它不能保证,也不知道CompositeForm是否实现了Composite 接口中的所有方法。不过这种方法的好处也很明显--简单,而且不需要额外的帮助类或者方法来支持。

   

用属性检测来实现接口

 

/*
interface Composite {
function add(child);
function remove(child);
function getChild(index);
}
interface FormItem {
function save();
}
*/
var CompositeForm = function(id, method, action) {
//声明必须实现 'Composite','FormItem'接口
 this.implementsInterfaces = ['Composite', 'FormItem'];
...
};
...
function addForm(formInstance) {
if(!implements(formInstance, 'Composite', 'FormItem')) {
throw new Error("Object does not implement a required interface.");
}
...
}
// The implements function, which checks to see if an object declares that it
//接口检测
// implements the required interfaces.
function implements(object) {
for(var i = 1; i < arguments.length; i++) { // Looping through all arguments
// after the first one.
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; // An interface was not found.
}
}
return true; // All interfaces were found.
}

从上面的例子看到,CompositeForm 中声明了该类必须要实现composite,formitem接口,检测没有实现,则抛出Error。

这种实现方式的优点如下:

1.记录了该类实现了哪几个接口。

2.如果该类没有实现它声明的接口,则会报错,这样,我们就可以强制性地让其他程序员必须实现这些接口。

但是,这种实现方式的缺点也很明显:

我们保证了实现类必须声明接口,但不能保证实现类一定实现了接口的所有方法。

 

3.特征法

  什么是特征法呢?打个比方,如果我们看见一种动物它走起来像鸭子,叫声像鸭子,生活习性也像鸭子,那么我们就认定它就是鸭子。用编程的角度来讲就是:如果一个类它声明了接口,而且实现了接口的所有方法,我们就认定了它实现了这些接口。容易理解吧~看例子:

// Interfaces.
var Composite = new Interface('Composite', ['add', 'remove', 'getChild']);
var FormItem = new Interface('FormItem', ['save']);
// CompositeForm class
var CompositeForm = function(id, method, action) { 
//implements Composite, FormItem’s method
...
};
...
function addForm(formInstance) {
Interface.ensureImplements(formInstance, Composite, FormItem);
// This function will throw an error if a required method is not implemented,
// halting execution of the function.
// All code beneath this line will be executed only if the checks pass.
...
}
 
/**Interface class**/

 1 // Constructor.
 2 var Interface = function(name, methods) {
 3 if(arguments.length != 2) {
 4 throw new Error("Interface constructor called with " + arguments.length +
 5 "arguments, but expected exactly 2.");
 6 }
 7 this.name = name;
 8 this.methods = [];
 9 for(var i = 0, len = methods.length; i < len; i++) {
10 if(typeof methods[i] !== 'string') {
11 throw new Error("Interface constructor expects method names to be "
12 + "passed in as a string.");
13 }
14 this.methods.push(methods[i]);
15 }
16 };

 

// Static class method.
Interface.ensureImplements = function(object) {
if(arguments.length < 2) {
throw new Error("Function Interface.ensureImplements called with " +
arguments.length + "arguments, but expected at least 2.");
}
for(var i = 1, len = arguments.length; 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.");
}
}
}
};

上面的例子中,帮助类Interface约束了实现类必须声明接口,实现接口的方法。 如果读者打算用设计模式来开发复杂的系统,我想这种方式会带来很大的好处。因为,对于大型的系统而言,会有很多程序员会一起参与开发,怎样让程序员更容易交流?代码质量更高?效率更高呢?当然就是规范了,接口就定义了一系列规范,其作用不言而喻。

下面再提供一个使用接口的范例:

  使用接口前的代码:

// 该类持有一个TestResult Object,并有了一个renderResults方法把测试结果输出到页面
var ResultFormatter = function(resultsObject) {
if(!(resultsObject instanceOf TestResult)) {
throw new Error("ResultsFormatter: constructor requires an instance "
+ "of TestResult as an argument.");
}
this.resultsObject = resultsObject;
};
ResultFormatter.prototype.renderResults = function() {
var dateOfTest = this.resultsObject.getDate();
var resultsArray = this.resultsObject.getResults();
var resultsContainer = document.createElement('div');
var resultsHeader = document.createElement('h3');
resultsHeader.innerHTML = 'Test Results from ' + dateOfTest.toUTCString();
resultsContainer.appendChild(resultsHeader);
var resultsList = document.createElement('ul');
resultsContainer.appendChild(resultsList);
for(var i = 0, len = resultsArray.length; i < len; i++) {
var listItem = document.createElement('li');
listItem.innerHTML = resultsArray[i];
resultsList.appendChild(listItem);
}
return resultsContainer;
};

使用了接口类之后的代码

// ResultSet Interface.
var ResultSet = new Interface('ResultSet',['getDate', 'getResults']);
// ResultFormatter class, after adding Interface checking.
var ResultFormatter = function(resultsObject) {
Interface.ensureImplements(resultsObject, ResultSet);
this.resultsObject = resultsObject;
};
ResultFormatter.prototype.renderResults = function() {
...
};
从上例中应该可以看出,重构后的代码可读性提高了?更重要的是,很多设计模式也依赖于接口,如:工厂模式,命令模式,油漆工模式等…接口只是我们为JavaScript设计模式铺平了道路…Amazing will coming soon..Contact with me:timo.li.icon@gmail.com

posted on 2010-10-11 12:42  timo.li  阅读(495)  评论(0编辑  收藏  举报

导航