Sé que ha pasado más de una década desde que me lo pidieron, pero solo pensé en esto por enésima vez en mi vida de programador, y encontré una posible solución que no sé si todavía me gusta por completo. . No he visto esta metodología documentada antes, así que la llamaré el "patrón de dólar público / privado" o el patró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]);
El concepto utiliza una función ClassDefinition que devuelve una función Constructor que devuelve un objeto Interface . El único método de la interfaz es el $
que recibe un name
argumento para invocar la función correspondiente en el objeto constructor, cualquier argumento adicional pasado despuésname
se pasa en la invocación.
La función auxiliar definida globalmente ClassValues
almacena todos los campos en un objeto según sea necesario. Define la _$
función para acceder a ellos name
. Esto sigue un breve patrón get / set, por lo que si value
se pasa, se usará como el nuevo valor de la variable.
var ClassValues = function (values) {
return {
_$: function _$(name, value) {
if (arguments.length > 1) {
values[name] = value;
}
return values[name];
}
};
};
La función definida globalmente Interface
toma un objeto y un Values
objeto para devolver un _interface
con una sola función $
que examina obj
para encontrar una función con el nombre del parámetro name
y la invoca values
como el objeto con ámbito . Los argumentos adicionales pasados $
se pasarán en la invocación de la función.
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;
};
En la siguiente muestra, ClassX
se asigna al resultado de ClassDefinition
, que es la Constructor
función. Constructor
puede recibir cualquier cantidad de argumentos. Interface
es lo que obtiene el código externo después de llamar al constructor.
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;
}());
No tiene sentido tener funciones no prototipadas Constructor
, aunque podría definirlas en el cuerpo de la función del constructor. Todas las funciones se llaman con el patrón de dólar público this.$("functionName"[, param1[, param2 ...]])
. Se accede a los valores privados con el patrón de dólar privado this._$("valueName"[, replacingValue]);
. Como Interface
no tiene una definición para _$
, los objetos externos no pueden acceder a los valores. Dado que cada cuerpo de función prototipada this
se establece en el values
objeto en función $
, obtendrá excepciones si llama directamente a las funciones hermanas Constructor; El patrón _ $ / $ también debe seguirse en los cuerpos de función prototipados. Debajo de la muestra de uso.
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");
Y la salida de la consola.
classX1.valA is invalid.
classX1.valA: undefined
classX1.valA is valid.
classX1.valA: v1
classX2.valA: v2
El patrón _ $ / $ permite la total privacidad de los valores en clases completamente prototipadas. No sé si alguna vez usaré esto, ni si tiene fallas, pero bueno, ¡fue un buen rompecabezas!