これが尋ねられてから10年以上経過していることはわかっていますが、プログラマーの人生でn回目のことを考えただけで、まだ完全に好きかどうかわからない可能性のある解決策を見つけました。この方法論はこれまでに文書化されたことがないので、「プライベート/パブリックドルパターン」または_ $ / $パターンと名付けます。
var ownFunctionResult = this.$("functionName"[, arg1[, arg2 ...]]);
var ownFieldValue = this._$("fieldName"[, newValue]);
var objectFunctionResult = objectX.$("functionName"[, arg1[, arg2 ...]]);
//Throws an exception. objectX._$ is not defined
var objectFieldValue = objectX._$("fieldName"[, newValue]);
この概念では、Interfaceオブジェクトを返すコンストラクター関数を返すClassDefinition関数を使用します。インターフェースの唯一のメソッドは、コンストラクターオブジェクトの対応する関数を呼び出すための引数を受け取ることです。その後に渡される追加の引数は、呼び出しで渡されます。$
name
name
グローバルに定義されたヘルパー関数ClassValues
は、必要に応じてすべてのフィールドをオブジェクトに格納します。で_$
アクセスするための関数を定義していますname
。これは短いget / setパターンに従うので、value
渡された場合、新しい変数値として使用されます。
var ClassValues = function (values) {
return {
_$: function _$(name, value) {
if (arguments.length > 1) {
values[name] = value;
}
return values[name];
}
};
};
グローバルに定義された関数Interface
は、オブジェクトとオブジェクトを受け取り、パラメーターにちなんで名付けられた関数を見つけるために検査する単一の関数でValues
を返し、スコープ付きオブジェクトとしてそれを呼び出します。に渡される追加の引数は、関数の呼び出し時に渡されます。_interface
$
obj
name
values
$
var Interface = function (obj, values, className) {
var _interface = {
$: function $(name) {
if (typeof(obj[name]) === "function") {
return obj[name].apply(values, Array.prototype.splice.call(arguments, 1));
}
throw className + "." + name + " is not a function.";
}
};
//Give values access to the interface.
values.$ = _interface.$;
return _interface;
};
以下のサンプルでは、が関数であるClassX
の結果に割り当てられてClassDefinition
いConstructor
ます。Constructor
引数はいくつでも受け取ることができます。Interface
コンストラクターを呼び出した後に外部コードが取得するものです。
var ClassX = (function ClassDefinition () {
var Constructor = function Constructor (valA) {
return Interface(this, ClassValues({ valA: valA }), "ClassX");
};
Constructor.prototype.getValA = function getValA() {
//private value access pattern to get current value.
return this._$("valA");
};
Constructor.prototype.setValA = function setValA(valA) {
//private value access pattern to set new value.
this._$("valA", valA);
};
Constructor.prototype.isValAValid = function isValAValid(validMessage, invalidMessage) {
//interface access pattern to call object function.
var valA = this.$("getValA");
//timesAccessed was not defined in constructor but can be added later...
var timesAccessed = this._$("timesAccessed");
if (timesAccessed) {
timesAccessed = timesAccessed + 1;
} else {
timesAccessed = 1;
}
this._$("timesAccessed", timesAccessed);
if (valA) {
return "valA is " + validMessage + ".";
}
return "valA is " + invalidMessage + ".";
};
return Constructor;
}());
プロトタイプ化されてConstructor
いない関数をで作成しても意味がありませんが、コンストラクター関数の本体で定義できます。すべての関数は、公開ドルパターンで 呼び出されますthis.$("functionName"[, param1[, param2 ...]])
。非公開の値は、非公開のドルパターンで アクセスされますthis._$("valueName"[, replacingValue]);
。にInterface
は定義がないため_$
、外部オブジェクトから値にアクセスすることはできません。プロトタイプ化this
された各関数本体はfunction内のvalues
オブジェクトに設定されるため、$
コンストラクター兄弟関数を直接呼び出すと例外が発生します。_ $ / $パターンがあまりにも試作関数本体に続いする必要があります。以下は使用例です。
var classX1 = new ClassX();
console.log("classX1." + classX1.$("isValAValid", "valid", "invalid"));
console.log("classX1.valA: " + classX1.$("getValA"));
classX1.$("setValA", "v1");
console.log("classX1." + classX1.$("isValAValid", "valid", "invalid"));
var classX2 = new ClassX("v2");
console.log("classX1.valA: " + classX1.$("getValA"));
console.log("classX2.valA: " + classX2.$("getValA"));
//This will throw an exception
//classX1._$("valA");
そして、コンソール出力。
classX1.valA is invalid.
classX1.valA: undefined
classX1.valA is valid.
classX1.valA: v1
classX2.valA: v2
_ $ / $パターンは完全にプロトタイプ化クラスの値の完全なプライバシーを可能にします。これを使用するかどうか、欠陥があるかどうかはわかりませんが、それは良いパズルでした。