Agregando skills modulares

Agregando skills modulares

Presentamos un nuevo enfoque a la hora de añadir caracteristicas y habilidades a nuestras clases de manera modular y funcional.

Continuando y con el propósito de mejora el anterior post, Mutar de manera inmutable, desarrollabamos una primera idea para mutar de manera inmutable, en este siguiente artículo os intentaré hablar y desarrollar una forma en la que podemos aproximar está capacidad que hemos añadido pero esta vez desde un punto de vista puramente de la programación funcional.

¿Por qué digo desde un punto de vista puramente funcional? Al comienzo del anterior post comienzo comienzo hablando que uno de los target iba a ser evitar diseños de patrones como el de cake pattern. Tal y como  se quedó el código, era precisamente un diseño de cake pattern, dónde si se realizaba un pequeño cambio en uno de los traits padres, se tenia que retocar en cada una de las clases hijas que lo extendiera.

Desarrollando un poco mas esta idea, al final del post haciamos una mixtura entre el trait ConfigTypes y CanCopy[T], extendiendo la capacidad de ser copiable y consolidandose en un sistemas de jerarquías propiamente de la programación orientada a objetos. Donde por ejemplo, en el caso de ConfigA heredaba la capacidad de su padre concreto CanCopy[ConfigA].

portada-website-blog

El resultado

Esto no sólo hace que ambos traits estén fuertemente ligados, lo que se traduce a una fuerte dependencia y lo que se traduce (como comentaba al comienzo) a que cualquier cambio de la herencia padre CanCopy se tenga que ver reflejado en sus hijos. Sino que además, semánticamente hablando, ser parte de ConfigTypes no implica necesariamente o no es equivalente a ser copiable o tener la habilidad de poder mutarse, una cosa no es intrínseca a otra. De hecho es un plus que nos daria el gusto poder darle.
 
Esto es como si por ejemplo pensaramos que un ordenador tuviera que llevar tarjeta gráfica dedicada por default, o que un iPhone viniera con cargador en su caja. Obviamente habría que comprar la tarjeta gráfica y el cargador, respectivamente, y añadirlo o conectarlo de alguna forma para darle ese plus a nuestro objeto base.
 
Pero es que todavía más, el hecho de estar todo tan mezclado y cada vez más, nos frena a querer modularizar ambos conceptos, lo que se traduce a ser difícil de escalar el módulo de CanCopy y/o el módulo de ConfigTypes.

Advertencia

Nivel de fumada alta. El contenido de este post trae razonamientos psycho loco con un nivel más o menos alto y que, aunque se intenta desarrollar y desmenuzar todo lo posible, puede llegar a partir cerebros.

Contenido

Inyectar dependencias

Inyectar dependencia es una forma de modelar comportamientos de manera funcional, evitando a toda costa generar un complejo sistemas de herencias. ¿Esto cómo se hace? Sencillo, aislaremos la funcionalidad o habilidad, definiremos ahi sólo y exclusivamente los métodos pensados para cumplir esos comportamientos que son propios de dicha habilidad que queremos incluir, haciendo que cuando se adquiera se comporte como se espera.

En este nuestro caso, que la habilidad de poder copiarse tenga el método copy() que proporciona el comportamiento de copiarse a dicha habilidad.

Dicho de manera general, debemos proporcionarle la habilidad al comportamiento para que se comporte como tal.

Al final, esto no es más que un ejercicio de separar la función con la parte que usa la función. Por ejemplo, la habilidad de saltar requiere la función de contraer los músculos. Entonces si una persona necesita poder saltar, debemos definir la habilidad de saltar incluyendo en ella la funcion de contraer los musculos que hace que salte como en teoría debe ocurrir la acción.

Pongamos los pies sobre el código

Actualmente nos quedamos en el siguiente punto:

Como vemos, la capacidad de copiarse está asignada como una capacidad intrínseca, ligada y fuertemente dependiente de la propia entidad ConfigA (ó ConfigB, ó configC).

Esto precisamente es lo que NO queremos, entonces vamos a hacerle un par de ajustes: Lo primero demos un paso atrás y rompamos esa herencia infernal entre ConfigTypes y CanCopy.

Como ya no va a formar parte de ningun tipo de jerarquia de padre-hijos, no tiene sentido parametrizar CanCopy a tipos que extiendan de sí mismos. ¿Por qué? Precisamente porque ya no se va a extender nunca más de CanCopy. Además, esto estaba para restringir a que sólo «heredara» la capacidad de copiarse los tipos ConfigTypes. Ahora lo queremos generalizar.

Entonces, ahora vamos a hacer eso de proporcionar la habilidad al comportamiento acorde con el comportamiento que en teoría deberia ocurrir para el ente al que le queremos agregar la habilidad de copiarse.

Si lo que queremos es que ConfigC tenga la habilidad de copiarse, debemos definir un mecanismo de copiado acorde con lo que ConfigC se espera que haga cuando se copie.

Así entendemos que el comportamiento de ConfigC para copiarse es una instancia como tal del comportamiento de copiarse para el tipo ConfigC, a la cual se le ha provisto del mecanismo de copiarse de la forma como se espera que un objeto de tipo ConfigC se copie.

Y ahora sí entonces, que cualquier instancia de ConfigC tenga la habilidad de copiarse es precisamente que use la habilidad cuyo comportamiento pueda darle capacidad de copiarse, es decir, que use aquella instancia CanCopy[ConfigC] (CanCopyde tipo ConfigC) mediante el método copy().

Comentario

Pfff.. y pensar que esto lo he escrito el mismo día que me pincharon la segunda vacuna del Covid19…

Así entendemos que el comportamiento de ConfigC para copiarse es una instancia como tal del comportamiento de copiarse para el tipo ConfigC, a la cual se le ha provisto del mecanismo de copiarse de la forma como se espera que un objeto de tipo ConfigC se copie.

Y ahora sí entonces, que cualquier instancia de ConfigC tenga la habilidad de copiarse es precisamente que use la habilidad cuyo comportamiento pueda darle capacidad de copiarse, es decir, que use una instancia CanCopy[ConfigC] (CanCopyde tipo ConfigC) mediante el método copy().

Entonces, y ya por cerrar el razonamiento, toda lógica que haga uso o que implique tener que copiarse, va a necesitar usar esa instancia. Por tanto, desde ahora y con el objeto de suavizar el lenguaje, definiremos estas instancias como skills, más concretamente skills de ConfigC.

Con esto, la forma en la que llamabamos el método copy de un objeto de tipo ConfigC, ha cambiado ligeramente, requiriendo por parámetro la skill. El resto es prácticamente igual.

Pfff… ¿Y hay que pasarle la skill CADA VEZ que quiera llamar al método de copiar? Vaya verborrea… Pues sí y no.

Hasta ahora es cuando hemos conseguido nuestro objetivo final. Dotar de la capacidad de copiarse y de manera modular, evitando patrones de tarta. Aquí ya acabaríamos. Sin embargo, no vamos a dejarlo así como así, todavía queda un toque que hará que nuestra implementación sea algo mucho más elegante: (Ring,.. ring…) Si? Implicitos al habla…

Skills (implícitos)

A ver, esto es como la vida misma: ¿Acaso cuando vas a saltar gritas o comentas que vas a saltar? ¿O le hablas a los musculos diciendoles que necesitas saltar y que contraigan los músculos? Claro que no.. (vaya, espero que no).

En efecto, de manera natural uno tiene naturalizado (valga la redundacia) el acto de saltar, y simplemente lo hace porque se realiza la contracción de los músculos y demás historias de manera implícita en tu cuerpo.

Pues aquí ocurre lo mismo, no le pasamos por parametro de manera explicita la skill, sino que la alojamos en el oscuro universo de los implicitos, y esta se cuela sin previo aviso por las llamadas implícitas que hagamos, cual acechador nocturno esperando ansioso en las sombras.

Buah.. ahora sí. Ahora podemos tener algo super bonito y elegante como teniamos antes, pero con el bonus de ser una skill modular.

Conclusión

En este post, hemos conseguido darle una vueltita a lo que desarrollamos en el post de mutar de manera inmutable:

Agregar skills …

En este caso, nos hemos abstraido al concepto de añadir  skills o capacidades a nuestras clases, es decir, hemos aprendido no solo atribuirle la forma de copiarse a una clase, sino que hemos alcanzado el nivel super saiyan 2 de como atribuir cualquier mecanismo o skill.

… de manera modular …

Pero no sólo hemos conseguido eso, sino que, lo hemos hecho de manera modular, evitando enredos en las herencias, que implican que un mínimo cambio en el padre, se desarrolle, como una bola de nieve que crece, en muchos cambios en cada uno de los hijos que lo heredan. O sea, super saiyan 3.

… y parametrizada

Y además, por ser modular y parametrizado por el propio tipo, es decir, que no es un CanCopy para todos, sino es un CanCopy de tipo T. Podemos definir distintos comportamientos de copy para distintos tipos (ConfigA, ConfigB, etc.) pero que la semantica del comportamiento como tal sea la misma. Super saiyan 4 bro…

Es decir, que la idea de copiarse el objeto ConfigX sea la de copiarse, pero internamente cada cual haga sus movidas que necesite para que ConfigX se copie de manera correcta o esperada al tipo ConfigX sin afectar a la forma en la que se copia o deberia copiarse el tipo ConfigA, o el tipo ConfigC: