estoy trabajando en un delegado pub/sub
patrón que puede funcionar para el problema que parece describir aquí. Creo que el objetivo es no terminar repitiendo el código en nuestro sub-objects
que ya hemos escrito en nuestros objetos principales, a menos que ese código sea completamente específico para ese módulo.
En general, creo que debemos alejarnos de esta idea de que debemos replicar el estilo de programación de "herencia clásica" que existe en otros idiomas, y simplemente aprovechar las capacidades que posee JavaScript de forma nativa. "Herencia" puede ser más sobre invocar propiedades y métodos en un contexto de ejecución (similar a como usamos .call()
o .apply()
), y no necesita crear una base de código masiva llena de módulos que "extienden" otros módulos, solo para terminar tratando ellos como objetos completamente aislados e individuales de todos modos.
Voy a poner pronto esto para usar en un proyecto más grande y ver cómo funciona cuando se trata de un código mucho más (advertencia: Sí, set
propiedades en __proto__
objetos):
// Global automatic reference counting
var referenceCount = 0;
// Core constructor (a factory function for objects)
function Class(obj) {
// Represents `this` (the invoking object)
var This = {};
// Allow the inheritance of an object or object properties when `this` is defined
for(var k in obj)
This[k] = obj[k];
// Basic `setter` method for `this`
This.publish = function(obj) {
for(var k in obj)
this[k] = obj[k];
};
// Basic `unsetter` method for `this`
This.unpublish = function(obj) {
if(obj in this)
delete this[obj];
else
for(var k in obj)
if(k in this)
delete this[k];
};
// Allow `this` to subscribe to the updates of another object
This.subscribe = function(obj) {
this.__proto__ = obj;
};
// Allow `this` to unsubscribe from the updates of another object
This.unsubscribe = function(obj) {
this.subscribe({}.__proto__);
};
// Allow `this` to permanently consume a property from another object
This.plagiarize = function(obj) {
var key = Object.keys(obj);
var v, i = key.length;
while(i--) {
v = key[i];
this[v] = obj[v][v];
}
};
// Apply a unique identifier which corresponds with the global reference counter (and increment it)
This.publish({ id: ++referenceCount });
// return to `this` a newly constructed object
return This;
}
La idea aquí es que usted escriba una API (o módulo si lo desea) para un tipo de objeto. Por ejemplo, su módulo puede ser una "API de vista" que contiene todos los métodos que pueden preocupar a un objeto de vista. Usted escribe esos métodos una vez, y nunca más. Al escribir el código dentro de esos métodos como si el módulo fuera cualquier objeto de invocación futuro (usando this
o un sustituto una vez definido para this
), puede evitar volver a escribir o anular las propiedades y los métodos más adelante en la línea. Por lo tanto, esta API se convierte efectivamente en el delegate
central (o puede llamarlo abstract class
si lo desea) para cualquier objeto concreto futuro que pueda estar interesado en su funcionalidad (por ejemplo, vistas). Ya escribimos API centralizadas en el servidor que actúan como delegados para los datos (la mayoría de las API web); ¿Por qué no deberíamos extender esta misma filosofía al lado del cliente para organizar mejor nuestros puntos de vista, modelos del lado del cliente y otros objetos?
La principal diferencia entre este patrón de delegado y los patrones de módulos tradicionales es que con este patrón normalmente terminará con un módulo algo largo que se ve y se siente como una API con métodos que pueden producir resultados muy diferentes dependiendo de el objeto invocado (y por lo tanto el contexto de ejecución), en lugar de tener muchos módulos semidestilados que tienen una combinación de lógica, métodos y propiedades únicas que finalmente hacen que repitan el código de los demás. Con este patrón, probablemente verá mucho menos lógica conditional
y nunca tendrá que volver a utilizar var self = this
; esto es lo que lo convierte en un delegado abstracto en lugar de un módulo concreto. Los frameworks como Backbone casi logran esto, pero finalmente no lo hacen. Backbone es un marco para la "herencia de objetos" y la organización modular del código, y eso es genial, pero creo que el mayor valor que agrega es en realidad el hecho de que ha hecho que las personas acuerden "algún estándar" (pero podría decir lo mismo aproximadamente CoffeeScript; eww ...).
El aspecto final de este patrón toca su primer ejemplo. Al incluir los métodos de acceso básico y la funcionalidad pub/sub
en el núcleo, podemos crear objetos que estén completamente sincronizados con sus padres cuando queramos y que tengan la capacidad de cortar caprichosamente sus relaciones, según sea necesario, si es necesario. He usado prototypes
para lograr esto, pero probablemente podrías pensar en otra forma también (usando constructores).Creo que da como resultado una versión más verdadera de pub/sub
, donde los editores no saben nada sobre sus suscriptores, y los suscriptores solo conocen a los editores cuando se suscriben y si se suscriben.
Para obtener más información al respecto, tengo un ejemplo más detallado y documentado, que describe algunos de los posibles casos de uso en una idea que he creado. Por supuesto, los nombres de las variables probablemente deberían cambiar a nombres que tengan más sentido en su proyecto. Usé estos nombres para expresar su propósito. Aquí está la esencia: https://gist.github.com/bennyschmidt/5069513
Estoy interesado en saber más acerca de otros patrones de diseño modular que todos ustedes han pensado. :>
Gracias. Mi código real está más cerca de tu segundo ejemplo, lo que me hace sentir mejor acerca de mi enfoque. Su ejemplo también me dio algunas ideas para mejorar también. – mpdonadio