パターンがさまざまな方法で実装されているのを見てきましたが、アイデアは単純です。
- いくつかの関数の名前を指定するだけのクラス(インターフェース)があります。(実際のインターフェースがインスタンス化するInterfaceというクラスが必要な場合があります。これにより、インターフェースのタイプはInterfaceになります)
- 次に、そのようなインターフェイスを実装する他のクラスがあります。つまり、この2番目のクラスには、インターフェイスで指定された少なくともすべての関数が必要です。
- 最後に、インターフェイスを実装するオブジェクトを受け取ることを期待する他の関数がどこかにあります。言及したサンプルコードでは、この関数はaddFormであり、「Composite」および「FormItem」インターフェイスを実装するオブジェクトを想定しています。
- 次に、この関数は、予期するインターフェイスのすべてのメソッドをループし、渡されたオブジェクトにもそれらのメソッドがあることを確認します。いずれかのインターフェイスのメソッドが関数に渡されたオブジェクトに見つからない場合、オブジェクトはインターフェイスを実装していないと判断し、例外をスローします。
オーバーヘッドが関係するため、このパターンは実用的ではないと感じる人もいるかもしれませんが、Javascriptにはインターフェースのネイティブサポートがないため、これはそれほど悪い解決策ではありません。また、Javascriptで小さなプロジェクトにインターフェースを使用するのはやり過ぎだと感じる人もいるかもしれません。
例
var Interface = function(name, methods) {
this.name = name;
this.methods = [];
if (methods.constructor == Array)
this.methods = methods;
else if (methods.constructor == String)
this.methods[0] = methods;
else
throw new Error("Interface must define methods as a String or an Array of Strings");
};
var InterfaceHelper = {
ensureImplements : function(obj, interfaces) {
// If interfaces is not an array, assume it's a function pointer
var toImplement = interfaces.constructor == Array ? interfaces : [interfaces];
var interface;
// For every interface that obj must implement:
for (var i = 0, len = toImplement.length; i < len; i++) {
interface = toImplement[i];
// Make sure it indeed is an interface
if (interface.constructor != Interface)
throw new Error("Object trying to implement a non-interface. "
+ interface.name + " is not an Interface.");
// Make sure obj has all of the methods described in the interface
for (var j = 0, interfaceLen = interface.methods.length; j < interfaceLen; j++)
if (!obj[interface.methods[j]])
throw new Error("Interface method not implemented. "
+ interface.name + " defines method " + interface.methods[j]);
}
return true;
}
};
var Drawable = new Interface("Drawable", ["onDraw"]);
var Surface = function() {
this.implements = ["Drawable"];
this.onDraw = function() {
console.log("Surface Drawing");
};
};
使用法
var myDrawableSurface = new Surface();
// Returns true
InterfaceHelper.ensureImplements(myDrawableSurface, Drawable);
// Returns false (Error thrown)
InterfaceHelper.ensureImplements(myDrawableSurface, Array);