Cómo Construir Frameworks (Sí, Leíste Bien: “Cómo”)

 Una  de las etapas más trascendentes en la carrera de un arquitecto es cuando le toca diseñar su primer framework. Ese momento es tan importante para él -por tratarse del primer framework que él creará en su vida smile_regular– como riesgoso para el proyecto -por tratarse del primer framework que él creará en su vida smile_embaressed

Quiero en estas líneas parar un poco la pelota contando primero -y antes de abrir el Eclipse, el Visual Studio o cualquier herramienta de modelado UML- qué es lo que llevó a la consideración de usar frameworks (qué es lo que viene estando mal cuando no se aplican), qué vienen a mejorar los frameworks (cómo es la vida después) y cómo concebirlos una vez que nos hemos decidido a incluirlos

En el Principio Era Dios. E Inmediatamente Después, los Fideos

Los estadíos tempranos de la programación no preveían -porque, logicamente, era temprano smile_regular– ciertas buenas prácticas. Entendamos por buenas prácticas aquellas que permiten alcanzar un fin igualmente bueno (o, por oposición, las que permiten evitar o mitigar algo malo)

Y qué era lo malo que ocurría en los primeros proyectos informáticos? Si se había tomado la decisión de -por ejemplo- almacenar los datos en determinada base de datos, o consumir alguna biblioteca de un tercero (para generar asientos contables, para tracear movimientos que eventualmente se pudieran auditar, etc)… el uso de esas APIs externas (la de la base de datos adquirida, o la contable o la de auditoría, etc) era directamente realizado in situ por los componentes de la aplicación. En todos y en cada uno de los lugares que esto era necesario 

image

Si te fijás en la parte izquierda de este diagrama, allí se concentra el código que vos (o tu organización, o bien terceras partes contratadas a tal fin) mantienen. Es éste el código que contiene la lógica de dominio de la aplicación, la cual no quiero llamar "lógica de negocio" para que no se malinterprete con "la capa de negocio" (que en este gráfico y por simplicidad vendría a estar inmersa en aquellas clases amarillas que se ven a la izquierda)

Finalmente, es destacable que el código que vos mantenés no es que llame a cualquiera de los componentes de terceros que usás sino en realidad a aquellos que componen la interfaz de programación (API, por application program interface). Estos últimos se encargarán de acceder al interior del framework del tercero, ahorrándote a vos la tarea de tener que entenderlo todo

Las contras hoy saltan a la vista pero voy a enunciar algunas acá (y seguro me voy a quedar cortina, debe haber muchas más):

  • Alto Expertise: empezar a usar las APIs nuevas no era para cualquiera. Había que entender detalles de bajo nivel de cómo fueron hechas (lo que se podía y lo que no, etc). Por consiguiente era común mandar a algunos empleados a un curso de entrenamiento en esta nueva tecnología, al tiempo de contratar en el mercado algún experto ya "bautizado a fuego" en las mismas (normalmente caro para la organización, pero a la vez seguro: iba a evitar que los demás se pongan a experimentar sobre la marcha). A posteriori, los que ya tenían cierto background armaban un entrenamiento rápido para el resto, y todo el mundo a picar código con las raras APIs nuevas
    Aprender nunca es malo, incluso es motivante!! Es necesario!! Lo que no es tan bueno es aprender bajo presión. No es tan bueno aprender para tener que usarlo en seguida (y bien) y por supuesto es bastante malo tener que estar en la misa y en la procesión (esto es, entender de la lógica de dominio de la aplicación y a la vez de las APIs externas que se están utilizando)
  • Acoplamiento Fuerte: de acá que en el título te decía "después de Dios, los fideos", porque a esto se lo conoce como código spaghetti o, en inglés, spaghetti code
    No sólo la decisión de ir con determinada pieza de terceros, sino también la forma en que ésta era usada era un candado a la mantenibilidad futura. Por ejemplo, te tiro un par de escenarios:
    • Después de los dos primeros años de uso de piezas de terceros, habiendo ya escalado su curva de aprendizaje, surgía la posibilidad de sustituirla por otra pieza de similar funcionalidad (por ejemplo la base de datos de otro fabricante, o cualquier otro mecanismo de almacenamiento de datos). La posibilidad de sustituirla podrá haber surgido, pero de ahí a concretarla ya era más difícil:
      • Los beneficios de la nueva plataforma debían realmente justificarlo ya que gran parte de la inversión hecha en la plataforma previa habrían sido tirados por la borda (quizás sin que todavía hubiera llegado su retorno o ROI, por return of investment)
      • Ir a revisar todas las líneas de código que tenían referencias a dicha API era un costo que normalmente, o bien se obviaba o bien se consideraba pero se calculaba mal (normalmente se pasaban por alto todos los programas regularizadores, tools de mantenimiento, etc, que usaban la API para regularizar cierta inconsistencia, y que por ende eran imprescindibles). Nada más ubicar todo este código era una tarea titánica, pero no tan titánica como modificarlo para referenciar a la nueva APIs (incluyendo testing, planes de contingencia, etc)
    • Incluso sin siquiera plantearse dejar de usar el componente externo, podía ocurrir -entre tantas posibilidades- algo como lo siguiente: como consecuencia de un problema de rendimiento o de escalabilidad, se decidía modificar la forma en que se venía usando la API. Por ejemplo, pasando un parámetro adicional que -según la documentación- mejorará las cosas. O, eventualmente, implementando una caché -y por consiguiente preguntando por resultados antes de invocar la API, o almacenandolos luego de invocarla-. Yo me acuerdo casi de memoria mega-operativos que duraban meses e involucraban a todos los responsables de módulos (Pagos, Servicios, Clientes, etc), los que tenían que notificarse de la modificación a aplicar y así garantizar que todos los programas fuentes bajo su responsabilidad quedasen afectados por ésta. Determinados operativos que recuerdo, inclusive, no podían pasarse a producción sino todo de una vez. Y esto generaba un rush (un quilombo, dicho en criollo) si en paralelo al cambio del operativo, algún programa fuente necesitaba otro cambio menor: había que asegurarse que el cambio del mega-operativo no entre a producción antes de tiempo (como polizón de otros cambios en paralelo). Un rush de la gran Wednesday

Podría seguir enumerando contras pero creo que con las dos que mencioné (expertise y acoplamiento) ya tenemos suficiente como para amargarle el día hasta a los teletubbies

 

Y Entonces Quedaron Tres: Tu Lógica de Dominio, los Componentes de Terceros y en el Medio Tu Framework 

Para combatir estos problemas que te describía recién, es que surgió la variante de introducir una pieza en el medio que oficie de intermediario entre la lógica de dominio (la aplicación, bah) que tenías que construir y las distintas piezas de terceros (o sea, las que te iban a hacer la vida más fácil, pero sin complicartela demasiado)

image

Acá la mano cambia completamente, veamosla por partes:

  • Ahora la lógica de dominio no "habla" directamente a esas piezas de terceros que facilitaban trabajos genéricos y a bajo nivel (es decir, más allá del dominio de la aplicación). En cambio, la lógica de dominio habla con APIs contruidas por vos o tu organización, que abstraen el acceso a esas piezas de bajo nivel de manera que la lógica de dominio no sepa, en última instancia, quién va a llevar a cabo esas tareas
  • La lógica de dominio no le va a hablar a cualquier clase de tu framework sino particularmente a aquellas clases que vos expongas hacia afuera como lo que va a pasar a ser tu API
  • A su turno, esta API, sea por configuración o en forma dinámica, le va a hablar a clases internas a tu framework que son las que a su vez se apoyarán en APIs de terceros (esas APIs de terceros que en el esquema anterior eran invocadas directamente por la lógica de dominio)
  • Revisemos este último punto: la API de tu framework puede estar implementada internamente en forma múltiple, de modo tal que cambiando -por ejemplo- parámetros de configuración puedas optar por ir a una base de datos, o bien a un archivo XML, o bien a un servicio Web o a un componente accesible mediante CORBA, etc, etc… etc!! smile_teeth

Los beneficios son mútiples, si bien no están excentos de algunas contras (ahora vamos a comentar algo al respecto). Como pros podemos enumerar lo siguiente:

  • Requerimientos de Expertise Reducidos (o Nulos!): como comentábamos antes, si incorporar una pieza de terceros implica lidiar con una API compleja y no excenta de terminar haciendonos meter la gamba, podríamos encapsular el acceso a la misma en otra API de complejidad simplificada (y quizás más ajustada a la lógica de dominio)
    Personalmente me gusta llamar, a este proceso de simplificación, comoditización (commoditization, tomando prestado un término de economía). En dos palabras, un commodity es un tipo específico de bien del que el mercado está saturado por lo que relanzarlo con cualquier factor de diferenciación no va a tener mayor impacto en la demanda. Los commodities -a excepción del petróleo, cuya producción suele estar al límite de la demanda- normalmente son baratos, más accesibles que otros bienes cuya disponibilidad es más limitada. Es por eso que me gusta robar este término de la jerga económica, ya que al encapsular detalles complejos de una API costosa de dominar en algo más simple, logra que un mayor número de desarrolladores pueda asir la nueva interfaz en plazos más breves. El código de estos desarrolladores, en tanto invoca a la API del framework, termina en última instancia invocando a la API externa
    El efecto neto creo que ni hace falta explicarlo: la ejecución terminó sacando provecho de la API externa sin que varios de los desarrolladores hayan debido pagar el costo de aprendizaje de la misma. Volviendo al ejemplo de la base de datos, toda la lógica de reservar un conector, abrir la conexión, ejecutar el acceso a la base y transformar los resultados a un formato más adecuado para la lógica de dominio puede ser encapsulado dentro de las fronteras del framework (en un rato voy a contarte, no obstante, de los riesgos de esto)
  • Lógica de Dominio Más Sencilla y Mantenible: es un corolario de lo anterior. Si toda la complejidad -necesaria en última instancia, aunque no relacionada directamente con lo que, desde el punto de vista del usuario, se espera que la aplicación realice- está escondida detrás de las paredes de esta plataforma que hacia afuera ofrece una interfaz más sencilla, la porción conocida como "lógica de dominio" -como consumidora que es de esta interfaz sencilla que el framework expone- dedica sus líneas de código más a esa lógica conocida como "de domin…" Caramba, que feliz coincidencia! smile_teeth
    Esta modularidad nos va a permitir también poder testear los componentes en forma unitaria (unit testing), práctica tal que afortunadamante hoy varias herramientas están disponibles -incluso en forma gratuita- para facilitar la creación de estas, así llamadas, unidades de testing
    Cuando de probar lógica de dominio se trata, las pruebas unitarias se llevan muy pero muy bien con otra técnica conocida como mocking (imitación), que también hoy cuenta con herramientas disponibles en cantidad y que nos habilita a simular recursos externos (la base de datos, un CICS transaccional allá atrás tirado en un mainframe, etc) de una forma rudimentaria. Esta claro que no son los originales pero si en realidad lo que queríamos en esta prueba unitaria era probar los componentes de dominio (los cuales sí deberán ser genuinos). Y toda vez que estos requieran acceso a otros componentes (sobre todo aquellos que sean complejos o bien en configuración o bien en ejecución o incluso en ambas), será allí donde el sustituto (el mock) entre en escena
  • Flexibilidad por Desacoplamiento: recién estaba hablando de mocking como una práctica que se veía facilitada por el hecho de que los componentes ganaban cohesión. Pero no es necesariamente el testing el que se va a ver beneficiado. Eso apenas en cuanto al ciclo de desarrollo del software. Pero el ciclo completo de vida del software incluye -claro- las piezas que están en ejecución. El hecho de que a través de toda la lógica de dominio usen componentes de un tercero (llamemoslo A) a través de un framework, permite que en paralelo podamos empezar a probar componentes alternativos de un tercero (digamos, B) con sólo reimplementar las interfaces que el framework, a través de su API, le exponía a la lógica de dominio!! Yo me voy a referir a esto en mayor detalle más adelante, cuando comente los patrones de diseño (especialmente Plantilla o TemplateEstrategia o Strategy). Por ahora que alcance don decir que no va a ser tan difícil cambiar de opinión respecto de lo que queramos tener allá al fondo ejecutando la lógica de bajo nivel (generar reportes, acceder a servicios remotos en forma transaccional, etc)
    Lo quiero ilustrar y dejar aclarado con el siguiente diagrama

image

Lo que estamos viendo acá es un escenario simplificado a diversos DAOs de la capa de acceso a datos. DAO (Data Access Object u Objeto de Acceso a Datos) se le dice al rol principal jugado por las clases de esa capa, que es el de llevar y traer cosas desde un repositorio, aunque desde una lógica funcional a la aplicación (por ejemplo, "traeme las Sucursales de Curicó", "los Clientes que deben más de tres cuotas", etc). Estos DAOs van a invocar, indirectamente, a una API externa que sea capaz de alcanzar al repositorio donde los datos son persistidos. Lo van a hacer indirectamente mediante una invocación directa al framework. Acá es donde se ve el beneficio del desacoplamiento: nosotros podemos probar ir por NHibernate, por ejemplo, que nos devolvería objetos planos o POCOs (por Plain Old CLR Objects). Eso nos ahorraría varias líneas de código de mapeo entre columnas de registros y propiedades de objetos. Ahora bien, si ese ahorro en tiempo de desarrollo luego se ve que se paga en rendimiento en tiempo de ejecución, podemos optar por sustituir el mecanismo de acceso por llamadas a procedimientos almacenados vía ADO.NET directo. Y para eso habría que tocar el DAO de cada módulo? No: como ya hemos eliminado el código spaghetti, la superficie de contacto con cada estrategia de acceso queda restringida a la implementación de la API de acceso a datos en nuestro framework. Encapsulada allí dentro y lejos del alcance directo de la lógica de dominio (estrictamente hablando se podría decir que los DAOs en realidad son híbridos, no pertenecen exclusivamente a la lógica de dominio; pero acépteseme aquí este abuso de confianza para no complicar demasiado las definiciones)
Alcanza, decíamos entonces, con reimplementar la API mediante otra estrategia de acceso a datos y, seguramente mediante configuración aunque no sería el único mecanismo (como veremos más adelante), a partir de allí toda vez que desde la lógica de dominio se acceda a la API de acceso a datos, la nueva estrategia basada en ADO.NET tomará el control
El día de mañana estos datos, o parte de ellos, están accesibles solamente mediante invocaciones a un sistema remoto (posiblemente en otra plataforma y/o ajenos a la organización) y entonces reimplementamos la API mediante invocaciones a servicios externos (por ejemplo a través de Windows Communication Foundation o WCF)
No tengo interés en caer en la tontera o la falacia de pensar que implementar cada nueva estrategia y pasarla a producción es joda y se hace en una semana. Tengo claro, al revés, que una vez que el sistema esté en producción, introducir innovaciones de esta naturaleza no sólo depende de la factibilidad tecnológica sino del oportunismo (sea éste comercial, político a nivel de la organización, etc). Me pasó, admito que es así. Sólo quiero recalcar que, en el escenario sin framework que habíamos visto al principio -donde llamadas a sistemas externos se hacía in situ en cada lugar de la lógica de dominio donde era necesario, el costo de modificar la aplicación por la introducción de una nueva estrategia de acceso a datos podía tornar, la sola idea, una quimera incumplible que habría matado a tal proyecto antes de empezar

  • Reusabilidad: las partes complejas de la aplicación van quedando encapsuladas en componentes que presentan una interfaz más simple, más ad hoc (voz latina por "directo al grano") para lo que la aplicación realmente necesitaba de las piezas de terceros (más allá de todo lo demás que éstas ofrecían, que si se quiere aprovechar a la fuerza, se termina complicándolo todo). Estas interfaces simplificadas que el framework ofrece son más rapidamente aprehendibles por aquellos desarrolladores que necesitan enfocarse realmente en el agregado de valor (pero al nivel  de lo que el negocio requería para ayer), la habilitación de nuevos canales (PDAs, web, ATMs, etc) y otros beneficios que implican tomar lo que hay y simplemente accederlo desde nuevos puentes (construídos a tal fin). Me parece que no hace falta aclarar más (por favor, si me equivoco me lo hacen saber y lo aclaro) que los frameworks promueven el reuso de aquellas terceras partes complejas en una forma tal que, si el reuso no es alcanzado, el éxito mismo del framework pasa a ser materia discutible
  • Extensibilidad: el punto que sigue es crucial. No necesariamente todos los frameworks ofrecen esto que te voy a contar pero sí te aseguro que -cuando lo ofrecen- la ventaja de usarlos adquiere magnitudes de un orden exponencial. Extensibilidad es posibilidad que el framework te brinda de agregar nueva funcionalidad en una forma que quede enlazada al mismo. Por ejemplo, imaginate que te regalan un Scalextric (un clásico juego de la niñez de carreras de autos, visitá el sitio y lloremos juntos de la nostalgia). La caja que recibís ya viene con ciertas piezas listas para que armes un determinado conjunto de pistas. No obstante, vos podés adquirir piezas de terceros (el cuenta-vueltas, la chicana, autos especialmente preparados, etc) para agregarle emoción a la cosa. Incluso algunos que se dan maña se terminan fabricando sus propias piezas. Como fuera que las consiguieses, una vez que las conectabas a la pista, eran inmediatamente reconocidas y asimiladas por este "framework" que era el Scalextric
    Esto tiene un nombre y es el de Principio de Hollywood (Hollywood Principle), que establece una conducta de "no me llame: yo lo voy a llamar a Usted". Dicho, ahora sí, en lenguaje para informáticos, significa que si construímos determinados componentes respetando cierta especificación (implementando equis interfaces, extendiendo equis clases y, en concreto, poniendole lógica a equis métodos), cuando el framework esté en plena ejecución va a terminar llamando a nuestras extensiones, que van así a agregarle valor al valor que ya el framework le agregaba a nuestra aplicación. Pavada de círculo virtuoso! thumbs_upsmile_teeth
    No quiero acá empezar a apilar conceptos a lo pavote, sólo decir que si alguna vez oíste hablar de Inversión de Control (Inversion of Control o IoC, premio al mejor lector al que haya leído mi post de hará más de un año atrás), está relacionado con esto mismo ya que lo que estos frameworks van a ser ahora quienes invoquen métodos de tus componentes
    La llegás a ver? Vos o terceros provéen parte de la lógica de aplicación mientras que el framework controla la ejecución de la misma. Existe un rango de frameworks conocidos como Contenedores Livianos (de aplicación, en inglés Lightweigth Containers) que, basándose generalmente en archivos de configuración, instancian objetos de estas clases que vos creás, para pasarles referencias a las mismas a aquellos frameworks que las van a controlar. Este proceso se conoce como Inyección de Dependencias (Dependency Injection) y voy a referirme a él más adelante

De nuevo, así como anteriormente al nombrar contras de no usar frameworks decidí no seguir enumerando, tampoco creo que valga la pena seguir mencionando beneficios aparejados por el uso de frameworks. Creo que todo el mundo a esta altura, haya comprado el concepto o esté en eso, está esperando lo principal: cómo se hace un framework!?

 

Del Pensamiento a la Acción: Cómo Construir Frameworks 

En esta sección voy a contarte aquellas prácticas que a mí me resultaron valiosas en las variadas oportunidades que tuve de construir frameworks propios, o las otras tantas que tuve de extender frameworks existentes. La mayoría de estas recomendaciones no han salido de mi cabeza sino que aparecen sobradamente en la literatura de hoy en día. Lo que sí son propias son las justificaciones de por qué estas prácticas valen la pena. Estas justificaciones no valen tanto por haber leído libros o white papers sino por haberlas comprobado empíricamente. Aquí están, éstas son:

  • Programación Basada en Interfaces (previamente Modeladas, claro!): ya desde el mismo modelado de procesos (generalmente a través de diagramas de secuencia en UML) se empieza a vislumbrar cómo las clases se van a ir relacionando. Pues bien, ya desde los tiempos del famoso libro Patrones de Diseño (Design Patterns, Addison-Wesley, 1995) de GoF (Gang of Four o la Banda de los Cuatro) se hacía mención a la ventaja de que si un objecto de clase A le tenía que pedir algo a otro de clase B, que entonces le hable en realidad a un objeto que implemente la interfaz IB en lugar de la clase B. Que, en todo caso, la clase B sea un implementación de la interfaz IB. La explicación es más que súper obvia: al no acoplar la clase A con la clase B directamente, si el día de mañana queremos, en una nueva clase B’ rehacer los algoritmos de una manera más eficiente o al menos alternativa a la forma en que lo veníamos haciendo, alcanzará con hacerle llegar al objeto de la clase A la referencia al objeto de clase B’, sin tocar absolutamente nada de la lógica de A (por el contrario, si la clase A tenía referencias explícitas a la clase B, entonces B’ iba a tener que extender B -algo que no siempre va a ser posible si los métodos de B no son virtuales o, dicho en Java, llevan el modificador final indicando que no se pueden reescribir-. Acá se me puede achacar que entonces alcanza con hacer los métodos de B virtuales, de manera de siempre poder sobreescribirlos. El problema es que el creador de la clase B puede no querer que eso ocurra (por las razones que sea: porque viola el encapsulamiento de la clase, porque puede abrir la puerta a inconsistencias, etc)
    Las cuentas claras conservan la amistad: excepto que a la clase A le interese especialmente que sea un objeto estricta, garantizadamente de clase B quien le sirva, debería manejarse con referencias a objetos que implementan la interfaz IB y listo
    En la práctica, a veces como arquitectos estamos ya programando un cachito, generalmente haciendo una rápida Prueba de Concepto (Proof of Concept o PoC) y si nos dicen "wait, primero modelate todas las interfaces prolijamente, inyectactale a las clases A todas las referencias a sus interfaces IB para que no se acoplen, etc" se nos puede demorar mucho una PoC que sólo pretendía validar o refutar rápido una idea. Yo lo que hago en ese caso es darle para adelante como viene, sin complicarme demasiado la vida interfaceando al momento de la PoC, pero luego, cuando la PoC exitosa va a dar lugar a un código más limpio y desacoplado, aprovecho las ventajas de Refactorización (Refactoring, técnica para mejorar la calidad del código -en términos de claridad, mantenibilidad- sin modificar su comportamiento) incluídas en Eclipse o Visual Studio, que a la vez me permiten revisar las referencias a la vieja clase concreta B y sustiuirlas por referencias a la interfaz IB extraída
    Antes de dejar este punto quería comentar que algunos autores recomiendan aplicar esto no sólo con aquellas dependencias entre clases que se inyectan al momento de ser creardas, sino también en el pasaje de argumentos de entrada y salida o valores retornados de los métodos
  • Cuando Convenga, Limitar la Práctica Anterior a Clases Abstractas en lugar de Interfaces: ocasionalmente esa libertad que el programar contra interfaces (el principio anterior) te brindaba puede estar abriendo la puerta más allá de lo que te interesa que se abra, y quisieras poder poner ciertos límites allí. Por ejemplo, quisieras que cuando la clase A llame a la instancia de IB para que le devuelva un resultado, A pueda esperar que dicho resultado contemple que los argumentos que A pasó hayan sido validados, y además que el resultado venga formateado en una forma en especial (redondeado a dos decimales, etc)
    En esos casos la medida que se suele tomar es no definir la interfaz IB sino definir una clase B, abstracta, donde algunos de los métodos públicos ya vienen no sólo declarados sino también definidos. La sutil diferencia entre declarar y definir se remonta a los tiempos del lenguaje C: la declaración tiene que ver con los argumentos de entrada y salida, sus tipos de dato, junto con el nombre del método (función se llamaba en C porque existían en forma aislada: no pertenecían a una clase en particular). La definición, en cambio, es todo aquello que pongas entre las llaves { y }
    La clase abstracta B es tan solo una implementación parcial de la interfaz IB que no fue, y al igual que ésta, no se puede instanciar directamente sino que se debe extender una clase concreta B2, la cual deberá completar los métodos que falten (definidos como abstractos, claro, en la clase abstracta B). La clase A puede darse el lujo de no saber que B2 (o quizás B3, otra extensión de la clase abstracta B) es la clase concreta a la cual le pide métodos. Basta con que A sepa que está lidiando con una instancia de la clase abstracta B. Y acá empieza entonces la rebelión en la granja (y no precisamente la granja de servidores):
    "Ey, Diegum, pero eso viola el encapsulamiento en la medida en que A asume parte de lo que B hace!". La respuesta a eso es "sí, lo sabe y justamente porque lo sabe es que prefiere explícitamente acoplarse a esa funcionalidad parcial que B provée". La clase A todavía sigue siendo agnóstica del resto de la implementación hecha sobre la clase abstracta B que recibió. Se dice entonces que el grado de acoplamiento entre A y B es parcial, en la medida en que cualquier día podemos crear una nueva extensión de B y hacérsela llegar a A sin que A tenga que modificar su lógica (claro, excepto que A instancie -por ejemplo- B2 directamente y mañana queramos que use B3; los frameworks de Inyección de Dependencias ayudan a combatir esto, lo mismo que el patrón FactoryFábrica– lo hace)
    Que A esté parcialmente acoplada con B, no es tan malo como estar totalmente acoplada (en el sentido que la superficie de contacto es menor) pero si -a pesar de que la clase A tenía la necesidad, de que B se comporte de determinada manera- tiene su parte fea: la clase B no va a poder modificarse libremente sino que deberá mantener el compromiso asumido con A y las restantes clases que confiaron en B por lo que B era en su momento. Como sea, esta técnica de usar una implementación parcial como una forma de limitar un poco la libertad que una interfaz sugiere es en verdad un patrón de diseño muy popular de GoF conocido como Plantilla (Template Pattern) o Método Plantilla (Template Method Pattern)
    La forma de garantizar que esos métodos que ya vienen definidos no puedan ser sobreescritos en una extensión de la clase B, se logra anteponiendo la palabra final a la declaración, si estás codificando en Java, o no definiendolo como virtual en el caso de C#. En otras palabras, por default en Java todo método se puede sobreescribir en tanto que en C# al revés: ningún método se puede sobreescribir a no ser que explícitamente el autor del método lo habilite
  • Mejor Aún: el Patron Estrategia (Strategy Pattern): si compraste la idea que vendí en el punto anterior, dejame por un adicional ofrecerte ahora el combo completo. Te planteo el siguiente escenario: vas al banco a pedir un préstamo. Les decís cuánta plata necesitás y ellos a partir de ahí te proponen prestarte ellos esa plata, ofreciendote determinados mecanismos para que a lo largo del tiempo se las devuelvas. Todos estos mecanismos se basan en algo obvio y básico: vos mes a mes (o con alguna otra frecuencia) vas a ir cancelando parte de la deuda, pagando un monto que se imputará total o parcialmente contra la deuda del capital que te prestaron y/o la deuda del interés -la ganancia del banco por el servicio de haberte prestado la plata-. A grosso modo, esto se llama amortización de la deuda, pero existen diversos mecanismos de amortizar. El más común y el único que conozco cómo funciona es el francés, donde empezás garpando mayormente el interés (ya que la deuda de capital está en su punto máximo, y por ende el interés es mayor) y un cachito de capital. Al siguiente período, como cancelaste ese cachito de capital, la deuda de capital es algo menor por lo que el interés que te devenga también es más bajo. Ahora, vos volvés a pagar el mismo monto que habías pagado el mes anterior, con lo cual se cancela primero la deuda de interés (que es más baja esta vez) y el remanente de lo que gatillaste (consecuentemente algo mayor esta vez) se imputa contra la deuda de capital, que así nuevamente descenderá y generará el mismo efecto al mes siguiente. El sistema de amortización francés suele terminar en una última cuota donde pagás un puchito de interés por el puchito de capital que te quedaba, y el resto se imputa contra la deuda de capital que termina quedando en 0 (cero). Existen varios otros sistemas de amortización: está el alemán, el americano… y calculo que habrá otros (una buena explicación de cada uno está disponible acá y en español)
    Podemos perfectamente desarrollar una aplicación para que el agente del banco te muestre el desarrollo de tu crédito, usando el patrón Plantilla. Simplemente definís en la plantilla la parte común a todos los sistemas de amortización (tienen una frecuencia, una cantidad de cuotas y una deuda de capital y de interés). Luego subclaseás esa plantilla con la parte variable de cada sistema (devengamiento de cuota, forma de imputación a la deuda de capital y de interés, etc). Tendrás así tantas subclases como sistemas hayas implementado
    El patrón Estrategia te ofrece algo más piola aún que lo que el Plantilla te da (aunque implementarlo es un cachito más complejo). Acá tendrás una clase fija Prestamo que es la que hace todo aquello que es común a los distintos sistemas, en tanto que aquellas cosas que cada sistema haga pero a su manera, Prestamo se lo va a pedir a otra clase que juega el rol de, así llamado, estrategia. (ojo, dije rol: la clase se puede llamar JosecitoPeñaloza si se te da la gana y el compilador te acepta eñes como caracter válido en identificadores). En realidad, Prestamo le va a hablar a una interfaz llamada, ponele, ISistemaAmortizacion, pero tendrá inyectada la consecuente dependencia a la clase que corresponda (SistemaAmortizacionFrances, SistemaAmortizacionAleman, SistemaAmortizacionAmericano, SistemaAmortizacionPizzeriaLasCuartetas, etc)
    La gran ventaja de el patrón Estrategia sobre el Plantilla es que, en este último las dependencias son estáticas: la herencia se declara en tiempo de desarrollo. Por consiguiente, si durante la ejecución de la aplicación, mientras el empleado del banco y vos discuten por el crédito, a vos se te ocurre probar con el sistema alemán qué pasaría, a nivel interno la aplicación tendría que instanciar la subclase de Plantilla que corresponde a ese sistema, pasarle todos los campos (duración de crédito, tu nombre, etc) y calcular el desarrollo
    En cambio con el patrón Estrategia mucho de eso no es necesario: a la clase Prestamo que tiene toda la data y la lógica en común le reinyectás la estrategia germánica y simplemente recalculás el desarrollo. Entonces se dice que la dependencia es dinámica, ya que es en tiempo de ejecución donde realmente se va a saber si tu instancia de Prestamo depende del sistema alemán o de Las Cuartetas (aguante, loco, quiero comer…)
    El patrón Estrategia hace gala de un principio de buena programación orientada a objetos ya postulado por el Gang of Four en el libro que te contaba antes) que dice "favorecé más composición de objetos que herencia de los mismos"
    Toma un poquito más de tiempo implementar el patrón Estrategia que el patrón Plantilla
  • No Abusar del patrón Singleton: normalmente cuando se habla de arquitectura de software o de sus técnicas en general, ocurre mucho que si quien habla (o, en mi caso, escribe) no es cuidadoso en la forma en que expone sus conceptos, estos pueden ser interpretados a la ligera -particularmente por arquitectos junior– y ser causa de problemas posteriores. El refrán típico para esto es "el que aprende a manejar un martillo le ve a todos sus problemas forma de clavo". Y eso te puede pasar con cualquier artículo que leas, si el autor no fue lo suficientemente explícito en contar los pros y contras, o efectos colaterales de algunas técnicas que -se esperaba- iban a beneficiar
    Quizás la memoria no me está ayudando pero realmente no recuerdo un caso más patente de estos tiros por la culata como el caso del patrón Singleton. Vamos a ver: el Singleton tenía como fin evitar una proliferación de instancias de clases cuando en realidad basta con contar con una, accesible desde cualquier lugar, para que todo el resto de la aplicación la pueda referenciar. Esto tenía como beneficio acotar el consumo de recursos
    El patrón Singleton fue recibido con beneplácito por todos quienes entendieron el beneficio que portaba, y comenzaron a aplicarlo (comenzamos, bah: yo vivía en el planeta Tierra también). A los pocos años, varios reparamos en las contras que el Singleton se traía bajo el poncho, contras que el libro de GoF no menciona
    Voy a contar la contra principal: el Singleton va a terminar acoplado a toda aquella clase cliente que lo use (con lo cual, cuanto mayor uso de esta instancia única, mayor acoplamiento) inhabilitando cualquier posibilidad de extensión al mismo por herencia. La razón? Para acceder al Singleton lo que se hace es llamar a un método de clase del mismo (normalmente llamado getInstance() o GetInstance() según se trate de Java o .NET). Dicho método pregunta a un campo privado -nuevamente de la clase (al ser método de clase, está claro que no podría intentar acceder a miembros de instancia)- si ya contiene una referencia a una instancia (que será devuelta ipso facto) o si aún contiene null (en cuyo caso le asignará una nueva instancia al campo de clase y devolverá su referencia). De esta manera, todas las clases que invocaron a ClaseSingleton.GetInstance() recibieron una instancia de ClaseSingleton. Si mañana por equis motivo tenemos que subclasear ClaseSingleton, todas aquellas clases que venían llamando a ClaseSingleton.GetInstance() seguirán recibiendo una instancia de ClaseSingleton así le agreguemos a la subclase su propio método de clase GetInstance(). Por consiguiente, o vamos a tocar en todos lados que llamábamos a ClaseSingleton.GetInstance() (incluí en la cuenta de las horas/hombre las horas de testing, integración, etc), o metemos los garfios directamente en ClaseSingleton (que es lo que comunmente pasa pero está completamente desprovisto de elegancia por cuanto para contruir algo nuevo necesité destruir lo anterior)
    La forma de evitar esta contra de los Singletons, sin resignar el beneficio que proveían en cuanto a control de instancias es mediante la aplicación de los Contenedores Livianos a los que me refería cuando hablé de Extensibilidad. Estos contenedores, como decía anteriormente, generalmente se basan en archivos de configuración donde declaramos instancias asignándoles algún identificador y qué clase queremos que nos instancie cada vez que le pedimos dame un objeto de identificador "pirulo". El contenedor liviano usa la data que pusimos en el archivo de configuración para saber qué clase va a instanciar (algo que va a lograr dinámicamente por reflexión). Normalmente los contenedores livianos nos permiten, en ese mismo archivo de configuración, indicar si queremos que devuelva siempre la misma instancia para un dado identificador, o si preferimos que crée siempre una nueva
    Algunos contenedores livianos, los más potentes, nos habilitan a indicar hasta cuántas instancias de la clase queremos que genere, y a partir de allí que devuelva siempre alguna de éstas. Los contenedores generalmente se basan en un archivo de configuración aunque ofrecen otros mecanismos para enhebrar las clases
    Normalmente el contenedor liviano es accesible a través de una clase estática del mismo, lo cual genera un acoplamiento sobre el contenedor liviano que puede ser dificil de revertir. Por esta misma razón los contenedores nos ayudan a minimizar la cantidad de invocaciones al mismo, enhebrando un tejido de referencias entre las clases de la aplicación. Esto va a permitir que, con tener la referencia a ciertas clases clave de la aplicación, las otras sean accesibles como propiedades de las primeras. Esto se conoce como Inyección de Dependencias y es el motivo del siguiente punto:
  • Cómo Enlazar las Estrategias: Inyección de Dependencias (Dependency Injection): volvamos al diagrama que te mostraba más arriba respecto de dónde está parado tu framework, cómo éste expone interfaces hacia afuera, que son la fachada de una implementación interna, de la cual las clases clientes del framework desconocen sobremanera (es decir, puede que tengan una referencia a las mismas pero ni siquiera saben de su existencia ya que la tratan como una interfaz o clase abstracta que el framework hacé pública). Asimismo, estas clases internas del framework se apoyan en APIs de terceros, eventualmente intercambiables (por ejemplo, tu framework expone una fachada de acceso a datos que en última instancia puede apoyarse en una API de conexión a bases de datos, o bien otra API de parseo de XML, o una tercera de interacción con un mainframe, etc). De nuevo, estas implementaciones de tu framework que se apoyan en piezas de terceros pueden requerir cierta configuración respecto de la forma de usar los mismos
    Cómo se puede lograr ese entramado de clases donde unas van delegando la ejecución en otras? Explicitando la pregunta: cómo hace la lógica de dominio para obtener referencias a clases de la API de tu framework, y cómo hace tu framework para saber concretamente en qué frameworks de terceros se está apoyando?
    Afortunadamante los contenedores livianos provéen ese servicio mediante la Inyección de Dependencias. De esta manera yo declaro al identificador "dao" como una instancia única de ADONETDao, (una hipotética clase de mi framework que se apoyaría en ADO.NET) y luego voy por sus propiedades, declarando su propiedad Conector como "oracle". En el mismo archivo de configuración, declaro "oracle" como instancia de la clase System.Data.OracleClient.OracleConnection. Luego voy por sus propiedades, asignando el string de conexión a la propiedad ConnectionString, y así sucesivamente. Capisce? No está una masa? Mañana puedo decidir pasarme de Oracle a otra base de datos, sustituyento en el archivo de configuración la propiedad Conector del identificador "dao" por, por ejemplo, "mysql". Ni siquiera tengo que ir a la declaración de "oracle" sino que puedo agregar a continuación una declaración "mysql" (puedo no: la voy a tener que hacer porque si no el contenedor liviano va a chillar por la referencia no resuelta), y a "mysql" le instanciaré las propiedades que el conector ADO.NET para MySQL me reclame (aún si no tengo un conector ad hoc, puedo ir por OleDb u ODBC)
    Del mismo modo, en el entorno de testing (en tiempo de desarrollo, claro), si quiero probar la lógica de aplicación sin entrar en el bolonqui de acceder a la base de datos que se va a usar en producción, puedo tocar la declaración de "dao", de manera tal que en lugar de ser una instancia de ADONETDao, lo sea de XmlDao. Posiblemente en ese caso sus propiedades sean otras, por ejemplo la propiedad "Repositorio" puede tener la ubicación del archivo XML que contiene los datos que voy a usar en mis pruebas
    Una masa total con un beneficio adicional: los usuarios de nuestro framework pueden implementar las interfaces del mismo en sus propias clases, extendiendo así nuestro framework allá donde nosotros no hemos llegado), enlazando luego sus implementaciones nuevamente mediante la Inyección de Dependencias que el Contenedor Liviano ofrece. Por ejemplo, pueden querer reimplementar las interfaces del objeto de acceso a datos con un componente que se apoye en NHibernate
  • Más Puntos de Extensibilidad: Callbacks (Retrollamadas) y el Regreso del Principio de Hollywood: hasta ahora veníamos viendo la extensibilidad por el lado de la implementación de interfaces, extensión de clases abstractas (mediante el patrón Plantilla) y la composición de objetos (mediante el patrón Estrategia). Otra forma de habilitarle a los usuarios de nuestro framework la extensión del mismo es mediante suscripción a eventos que nuestro framework emita, de modo que cada vez que nuestro framework emita esos eventos, notifique a los usuarios suscriptos al mismo
    El concepto detrás de esta facilidad viene dado por un patrón de diseño conocido como Observador (Observer), que afortunadamente la plataforma .NET implementa desde el mismo motor de ejecución, a través de dos tipos de declaradores: evento (event) y delegado (delegate). Para una discusión a fondo recomiento el capítulo 6 del libro "Programming .NET Components, 2 Ed" de Juval Löwy (O’Reilly, 2005). Eventualmente un interesante puntero al tema está en MSDN, pero el libro de Löwy es acabadamente lo más
    Se podrá notar que ésta es una particular forma de Principio de Hollywood, distinta a la que el patrón Estrategia podía plantear por lo siguiente: en el caso de Estrategia, la clase invocadora sabe que está pidiendo la ejecución de un método. Lo que no sabe es quién lo está ejecutando, pero sí que el método va a ser ejecutado. En cambio, en el caso del patrón Observador, cuando el evento es emitido, el emisor no tiene idea de cuántos suscriptores van a actuar en consecuencia. No sólo no los conoce sino que hasta podría no haber ninguno!!
    Obviamente, esto no lo hace ni mejor ni peor: Estrategia y Observador son dos patrones diferentes pensados para resolver problemas diferentes. Por caso, se ve claramente que Estrategia es una forma más potente (más cara de lograr también) de desacoplar, respecto del patrón Plantilla. Pero en el caso de Observador, la naturaleza del problema mandará si aplica Estrategia o si aplica Observador. Raramente se pueda optar alternativamente por cualquiera de los dos para un mismo problema
    Eventos y Asíncronismo: la gestión ante un evento mediante delegados se lleva idealmente bien con el principio de ejecución asincrónica. Y es lógico que sea así: justamente al ocurrir que el emisor del evento no sabe cuántos suscriptores van a actuar sobre el mismo, si el modelo de ejecución fuera síncrono ocurriría que el emisor se quedaría bloqueado hasta que el último suscriptor se entere de la ocurrencia del evento y actúe en consecuencia. En cambio de esta forma, el emisor del evento se puede quedar tranquilo que los que se hagan eco del mismo no le van a impedir continuar con el desarrollo normal de sus actividades
    Dicho en otro nivel, el usuario no se va a tener que quedar mirando con cara de tonto como la aplicación quedó congelada mientras en la barra de título de la ventana se agrega ese inoportuno "(Not Responding)" ("No Está Respondiendo"). Por esto mismo, el patrón Observador y la ejecución Asíncrona suelen llevarse como Cul… esteeemmm… como Evento y Calzoncillo quise decir. Quizás un principio de discusión sobre el tema está disponible en MSDN, aunque de nuevo te digo, conseguite el Löwy 2da Edición y revisá el capítulo 7. Eventualmente, otro artículo algo más en profundidad que el anterior de MSDN es éste
  • Monitoreo: tu Framework Debe Dejar Tomarse la Presión: una ventaja estratégica que los frameworks ofrecen como derivado son puntos de intercepción para poder monitorear lo que está pasando por ellos y con ellos. Por ejemplo, si te están preocupando las demoras en aquellas transacciones que acceden al mainframe, pero no tenés claro si la demora está en el tráfico en sí o en el proceso de marshalling de parámetros/unmarshalling de resultados que rodean -temporalmente hablando- al roundtrip. Existen varios mecanismos para implementar mediciones de rendimiento. Desde el dedazo en las clases del framework que implementan la conversación con el mainframe (recurso al que vas a poder echar mano si las clases son tuyas); hasta la gestión de los medidores en forma asíncrona como resultado de la emisión de eventos en las distintas etapas del acceso al mainframe. La primera alternativa es fácil de implementar pero tiene mucha pinta de spaghetti. La alternativa asíncrona es la más poderosa, pero es más bravita de implementar (igual, es la que te recomiendo por lejos). Una tercera alternativa es la que propone el patrón Decorador (Decorator) que, graficamente hablando, envuelve los métodos originales en una capa aislante donde va la lógica de medición de rendimiento. Es mejor que los fideos pero, al igual que éstos, agrega su overhead (sobrecarga). Sin duda que es más mantenible que los fideos, pero todavía es mejor la versión asíncrona. Otras alternativas de intercepción en frameworks son las que se describen en el libro "Enterprise Solutions Patterns" (MS Patterns and Practices, 2003)

Y creo que con todo esto tenés como para entretenerte, si te acordás de tener en cuenta todas estas cosas, con el próximo framework que te toque construir. Acordate siempre que estas "pastillas" que te marqué recién se deben tomar en la dosis justa y no más allá. El que exagera la dosis y abusa de estos remedios suele terminar intoxicado. Pasemos entonces a las recomendaciones finales que se deben tener en cuenta antes de decidir crear un framework

 

Antes de Empezar, README.1ST 

  • Retorno de Inversión (ROI o Return of Investment): las aplicaciones que no consideran frameworks suelen ser menos mantenibles en el largo plazo. No obstante, suelen ser también más rápidas de construir más allá de que sean pasibles de alto acoplamiento (cuando no también de baja cohesión). Seguramente un framework, pensando en el largo plazo, va a ser la decisión más acertada por la simplificación y mantenibilidad que ofrecen. Ahora bien, si la aplicación no está pensada para largo plazo sino para una etapa intermedia, gastar recursos en planear un framework es en realidad malgastarlos. A no ser que, ojo al piojo, el framework lo estemos pensando no necesariamente para esa aplicación sino ya pensando en un roadmap de aplicaciones que se van a beneficiar en serie (y en serio, como suelo decir en este blog) del reuso. Esto suena hermoso, aunque es difícil de lograr porque no es fácil anticipar de antemano las necesidades que clientes del framework van a requerir de éste. De esto hablé alguna vez en el post La Reusabilidad en Crisis. La batalla por el reuso total quizás algún día se gane, lo importante es no abandonarla
  • Procesos de Desarrollo Ágiles: así como hoy en día es muy común desarrollar nuestros propios frameworks, tanto lo es que queramos hacer esto siguiendo un proceso ágil como eXtreme Programming, SCRUM u otros. Sólo te quiero preparar, si ése es tu caso, para ahorrarte frustraciones posteriores: va a ser más difícil que eches a andar el framework si lo estás desarrollando con un proceso de los ágiles. La razón? Estos procesos se basan en la generación frecuente de entregables que el usuario empieza a testear a las pocas semanas de comenzado el proyecto
    Hacete la idea que, al menos en las primeras iteraciones, va a ser muy difícil que puedas meter demasiada lógica para el framework. Lo vas a lograr, en cambio, en el mediano y largo plazo y a través de un persistente proceso de refactorización (refactoring) como el que te hablé más arriba
    Por favor leéme bien: no te estoy diciendo que Agilidad y Frameworks son el agua y el aceite. Te estoy diciendo que en las primeras iteraciones va a ser raro que esta pieza clave que hacés para el largo plazo ya esté casi terminada en el corto
    Obviamente, esta mala onda que te acabo de tirar smile_regular aplica si tanto framework como aplicación mediante proceso ágil se deben hacer en simultáneo. Quiero decir, si el framework ya lo tenías, joya
  • Problemas de Rendimiento (Performance) y/o Escalabilidad: la ventaja del encapsulamiento que los frameworks alcanzan está en la reducción de complejidad para los desarrolladores que van a construir lógica de aplicación. Sin embargo a veces esto puede ser un arma de doble filo. Lo que te voy a contar que pasó, pasó de verdad en un proyecto del que fui parte. El framework con que contábamos allí había logrado que desarrolladores que no sabían Java desarrollen lógica de negocio en una forma completamente visual. Tanto el framework como la herramienta visual estaban hechas en Java por un equipo especializado (al principio el equipo del framework excedía en número al equipo de lógica de aplicación, pero en el mediano plazo -cuando ya el framework era más robusto y usable- la cantidad de desarrolladores no Java creció, en tanto que al tiempo, con el framework ya casi terminado, los desarrolladores Java iban de a poco siendo reubicados en otros proyectos
    Desde ese punto de vista, una masa: yo doy fe que lo que te contaba que los frameworks traen como beneficio, se confirmó acá (no fue el único proyecto en que lo comprobé, afortunadamente). No obstante, la clave del éxito del framework marcó a su vez su principal derrota: los desarrolladores no Java, desconociendo la complejidad no sólo a nivel de lógica sino en tiempo ejecución que transcurría en las capas interiores, usaban los servicios que el framework proveía como si fuera joda, provocando no sólo congestionamientos en los servidores sino también una baja capacidad de reacción del lado cliente, provocando la ira de los usuarios por la pobre experiencia ofrecida
    Ilusamente se puede creer que la clave del arreglo pasa por tocar el framework, poniendo cachés y usando otras técnicas adicionales para mejorar el rendimiento y/o la escalabilidad, pero eso es una parte de la verdad: va a ser muy difícil que, del lado de la lógica de aplicación, no haya que replantear parte de la forma de usar el framework. Cosas que pasan
  • Alternativas: los frameworks son hoy una forma muy elegante de generar aplicaciones a escala, pero no son ni por lejos la unica forma de hacerlos. Veamos que más tenemos, aparte de ellos
    • Generadores de Código: presentan la misma ventaja que los frameworks en cuanto a que la complejidad queda encapsulada para el desarrollador de lógica de aplicación, pero no va a estar encapsulada en clases del framework sino como resultado del parseo de un Lenguaje de Programación Específico de Dominio (Domain-Specific Language o DSL). A veces el desarrollador de lógica de aplicación ni se va a enterar siquiera de este lenguaje ad hoc, si encima tiene la suerte de contar con una herramienta visual que le ahorra la tarea de aprender siquiera esa sintaxis
      El ejemplo de estos días (por favor, no el único) es XAML, una sintaxis XML donde se definen ventanas, controles, etc, que decanta luego en clases .NET que realizan esto mismo
      Aquellos desarrolladores de ventanas que sus patrones estén dispuestos a desembolsar unos morlacos más, van a poder gozar de productos como MS Expression o similares, que visualmente le permiten arrastrar controles a ventanas u otros componentes, generando el XAML en forma automática (que a su turno será usado para generar el código C# equivalente)
    • Fábricas de Software (Software Factories): es en verdad no una alternativa a los frameworks sino un supra conjunto de estos, que además incorpora generadores de código para consiguientes lenguajes específicos de dominio. Al compilarse estos, llamadas al framework son generadas como resultado. Son superiores a los frameworks en el sentido que si estos estaban hechos en .NET, o en Java, entonces la lógica de aplicación debía programarse en el respectivo entorno administrado. Las fábricas de software, en cambio, permiten que el lenguaje específico de dominio se compile en .NET, en Java o eventualmente en ambos e incluso otros (Transact-SQL, etc) de modo de sacar el máximo provecho de lo que distintas plataformas de ejecución provean
      Las fábricas de software son lo mejor de lo mejor pero… son -por ahora- casi imposibles de lograr. Además no te librás de crear el framework si aún no contás con él
    • Programación Orientada a Aspectos (Aspect-Oriented Programming o AOP): es un paradigma que presenta una evolución respecto del hoy altamente afianzado "orientado a objetos". No obstante, de momento, como lenguaje de programación no termina de ser un experimento (AspectJ, un lenguaje AOP basado en Java pero con compilador propio; Aspect#, una versión análoga -aunque menos potente- basada en C#, etc)
      AOP permite combatir directamente el código spaghetti (que el folklore aopeísta llama "código enredado" o "tangled code") mediante la definición de puntos de unión (joint points) donde avisos (advices) son capturados por interceptores (interceptors) que dan intervención así a diversos aspectos (aspects). Casos típicos son logging, avisos para monitoreo, verificaciones de privilegios de acceso, etc

 

Para Más Referencias

Esta entrada fue publicada en Software Architecture. Guarda el enlace permanente.

7 respuestas a Cómo Construir Frameworks (Sí, Leíste Bien: “Cómo”)

  1. Bruno dijo:

    Macho como te lo curras … (frase de un argento que hace 2 años vive @Madrid) Excelente post .
    Ya tengo un post por hacer pasar SI O SI, a muchos autodenominados SoftArch con los que hablamos en el día a día sobre DSL, SF, etc. Aunque (no es crítica, es my vision) creo que hoy las Software Factories están en un punto dulce.
    Me explico: hace unos años el concepto de una factoria de software se relacionaba con 20 tipos y 40 manos (en el mejor de los casos) que tiraban líneas de código como unos campeones, conceptos como la automatización de los procesos de desarrollo, eran una utopía. Sin embargo, hoy tenemos muchas herramientas que nos ayudan a crear herramientas para que nos sigan ayudando a trabajar (¡¡plop!!).
    Aunque lo que principalmente veo es que el problema más grande suele estar en la mente de algunas empresas que no saben aprovechar, una gran experiencia en un dominio específico de alguna rama de negocio, un buen grupo de profesionales que domine este tema, y muchos recursos mas. En otras palabras, tienen todas las condiciones para comenzar a industrializar un poco sus procesos de desarrollo de soft, pero les cuesta dar el 1er paso.
    Repito, Excelente post😀
    Saludos @ Madrid
    El Brunowww.elbruno.com

  2. Diego dijo:

    Jjajajajajjjajaja, muchas gracias, Bruno!!!  😀
     
    Bueno, todo depende de qué tan españolizado estaba tu amigo argentino. Porque si bien es España "currar" es trabajar… en Argentina "currar" es, en principio, robar y también remotamente se acepta como trabajar (ya que remotamente encontrás en Argentina un político que trabaje y no robe   :-P)
     
    Me alegro que puedas disfrutar el momento actual de las Software Factories. En el pasado TechEd (Junio, en Orlando) se presentó una sesión sobre SFs aunque el consultor de MS fue bastante sincero de lo que vale la pena y lo que no de las mismas… El público le tiró naranjas al pobre speaker   :-)   Puse un extracto acá: http://diegumzone.spaces.live.com/blog/cns!1AD5096D63670065!919.entry

  3. Gerardo dijo:

    Excelente post Diego, aca desde Perú siempre estoy al pediente de tus aportes
    Un Abrazo
     
    Gerardo Tataje

  4. Diego dijo:

    Muchisimas gracias por el apoyo, Gerardo!!
     
    Yo tengo allá en Perú a mi referente, de quien -al igual que tú- estoy pendiente de todo nuevo aporte: el Sr. Jorge "Gurú" Oblitas

  5. Diego dijo:

    Diego, felicitaciones por este excelente artículo. Realmente se notan tus conocimientos y además tenés mucha chispa para comunicarlos de manera ágil y divertida (y en puro porteño!😉.Se podría decir que soy "el target" del artículo ya que – para fortuna o desgracia de mis empleadores- estoy construyendo mi primer fwk de aplicaciones. Realmente la información que encontré acá es muy útil para arrancar bien en esto. ¿Puede ser que hace unos meses dieron una charla en el MUG, basados en los conceptos de este artículo? Yo asistí a esa charla y creo recordar haber visto los gráficos de "Codigo que vos mantenés". Los oradores fueron los magistrales Daniel Laco y Víctor Passador.Bueno, te mando un saludo y quedo a la espera de nuevas perlitas.Diego Cofré

  6. Diego dijo:

    no sabría decirte, tocayo
     
    no sería la primera vez que me "chorean" slides, tampoco  😀

  7. Lorenzo dijo:

    Muy buen artículo, he estado en empresas donde siempre niegan la necesidad de un framework e indican que ya hay cientos de ellos en internet…

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s