jueves, 25 de enero de 2018

Regla N°16 de la Ingeniería de Software: Por defecto todo es privado (principio de ocultación)


Esta semana me paso algo que definitivamente creo que merece una nueva regla. Me encontraba con la necesidad de modificar la implementación de una funcionalidad dentro de un componente de uso común, el motivo es porque se comportaba de forma errónea. Hasta allí todo era normal, una modificación de un componente que no debiera impactar en absoluto al comportamiento ni a la implementación de los sistemas que lo consumen. Cuando me di a la tarea, apareció una variable pública dentro de la clase, que además era parte fundamental de la implementación… y allí comenzaron todos los problemas.




El hecho que estuviera pública la variable implicaba que cualquier cambio hacia la misma afectaría a los módulos que ajenos al sistema que la consumieran (y en este caso así era). La funcionalidad estaba horriblemente implementada (y era incorrecta), y la variable francamente era innecesaria, sin embargo debiera mantener compatibilidad con la multitud de sistemas que la usaban. Aquí había dos opciones, mantener la variable y de alguna forma modificar a funcionalidad para que la sigan usando en los mismo términos (cosa que se me hacía un malabarismo completamente innecesario y perjudicial, a la vez que chapucero) o rediseñar la funcionalidad y solicitar a todos los sistemas que se adecuaran a ella, con lo que implicaba coordinar una liberación múltiple de sistemas. Una tercera opción es mantener los dos componentes productivos, el que tiene el error y el que no, pero eso implicaría un doble mantenimiento en supuesto caso que hubiera que mover nuevamente el componente.

Intentando comprender que motivos tenía el programador para crear un variable (que claramente tiene un ámbito privado), como publica, me doy cuenta que se comparte entre dos módulos del mismo componente, esto de por si era innecesario, pero no solo no se redujo el ámbito de la variable al nivel del componente, si no que se hizo totalmente pública.

He observado que esto es un problema muy común de muchos programadores noveles, escriben la palabra "public" por inercia al momento de crear una nueva funcionalidad, sin pararse realmente a pensar que están haciendo, y solo cuando lo meditan bien escriben "privado". A veces no se dan cuenta de esta circunstancia, otra veces piensan que es más sencillo, si todo es público, a veces incluso piensan que le dan cierto sentido de "libertad", cuando lo que se hace abrir puerta al desastre (la “libertad” la da en todo caso, compartir el código fuente, no habilitar métodos privados como públicos).

La verdad es que se lo único que se está haciendo es romper el "principio de ocultación". El principio de ocultación indica que cualquier implementación interna de una clase debe estar oculta para los consumidores, no debiera mostrarse variables de ningún tipo, si no que la interacción y la modificación de la instancia de una clase se hacen mediante mensajes (representados genialmente a través de métodos, o propiedades). Generalmente esto se consigue mediante el encapsulamiento, agrupar dentro de la clase todo lo que le de identidad como tal (tanto funcionalidad, como datos), y solo exponer los mensajes necesario para interactuar con ella.

Partamos de una clase que tiene todo privado. Ok… ese tipo de clase no sirve de nada, puesto que necesita comunicarse con el exterior, o más bien el exterior necesita comunicarse con ella para que sea útil, eso es porque la clase tiene una responsabilidad (debemos asegurarnos que solo y exclusivamente tenga una). Debemos asegurarnos de exponer solo los métodos (o propiedades), mínimas indispensables para poder comunicarse, cuanto menos sean mucho mejor será.

Con esto estoy garantizando otro principio, que es el del desacoplamiento. Esto es que las clases dependan muy poco entre ellas y de sus implementaciones particulares, de forma que pueden ser sustituidas fácilmente por otro tipo de la implementación, o incluso con otras clases, sin mayor impacto en sus consumidores. Esto era imposible en mi escenario, el hecho que los sistemas usaran una variable pública implicaba que dependía mucho de cómo internamente se usaba ese variable, impidiendo cualquier sustitución, con los que estaban demasiado acoplados.

Existen los siguientes grados de ocultamiento (en la mayoría de los lenguajes):

  • Privado: El más restrictivo. Cuando más privado sea todo más desacoplado será, Solo la misma clase puede usar un método privado.
  • Protegido: Solo puede usarlo la misma clase o sus descendientes. Por lo que por lo menos garantizamos que no hay un consumo indeseado desde el exterior de clase. Si no hay algún motivo para hacerlo protegido, debiera seguir siendo privado, es más fácil cambiar un método de privado a protegido, que al revés.
  • Interno: Aquí entramos un poco un terreno más público. Indica que todos los módulos (clases) que pertenezca al mismo componente, podrán usar el método, en este escenario seguimos teniendo el control de lo que es público y privado, ya que siguen siendo llamadas que ocurren en el interior de nuestro componente.
  • Público: Aquí ya estamos en el ámbito publico totalmente, cualquiera podría usar el método o la variable en cualquier momento y forma, con lo que sí está mal expuesta (como nuestro caso), los métodos que la implementan lo harán de forma incorrecta, dificultando el poder reemplazar el componente por otro más optimo debido al acoplamiento sufrido.
  • Amistoso "friendly”: Este es algo curioso, es cuando un componente tiene “derecho” de acceso a un método privado de otro componente. Tenemos en este caso dos componentes que no tienen nada que ver entre ellos y sin embargo uno puede acceder a los métodos privados del otro. Aquí hay un problema de cohesión, debido a que si es realmente necesaria la “amistad” seguramente debieran ser el mismo componente y no dos componentes separados. Además hay un problema de acoplamiento debido a que estamos haciendo que un componente que no tiene nada que ver con otro, conozca detalles de su implementación interna.

En definitiva debemos usar el modificador más restrictivo siempre e ir haciendo publico según tengamos la necesidad, cuestionándonos siempre si realmente es necesario el cambio. Esto reducirá el acoplamiento de los componentes y nos facilitara el mantenimiento en nuestros sistemas.

jueves, 11 de enero de 2018

Regla N°15 de la Ingeniería de Software: Haz que las cosas sucedan

"... En la guerra puede haber cambios de circunstancias ante las cuales el general debe reaccionar. Decir que debe esperar las órdenes del soberano para decidir es como esperar las órdenes para apagar un fuego. Antes de que lleguen las órdenes pertinentes, todo estará reducido a cenizas." Sun Tzu, el arte de la guerra.

Una de las cosas que más afectan al éxito del desarrollo de un sistema es la pasividad y falta de proactividad. Esto es, principalmente, quedarse esperando a que las cosas pasen por si solas.

Muchas veces existen escenarios de incertidumbre en que no queda claro que es lo tenemos que hacer en cuanto al sistema que se está construyendo, esto puede ser por muchos motivos, puede ser una falta de visibilidad del objetivo real o que no se nos ha comunicado apropiadamente.

Pero es necesario comprender que mientras el sistema no esté finalizado (satisfaga las necesidades del cliente), hay tareas pendientes que hay que resolver y es responsabilidad de cada integrante del equipo conocer cuáles son y conseguir se lleve a cabo. Es responsabilidad de cada uno hacer que las cosas sucedan.


Los escenarios por los se puede caer en la pasividad son, entre otros, los siguientes:

  • No existe una fecha de finalización del proyecto. Esto provoca que no se dé la suficientemente importancia a acabar el proyecto, siendo laxos con los compromisos y avanzando de manera inadecuada e irregular.

    Toda tarea debe tener una fecha. Cada vez que surja una tarea, ponle una fecha, si no puedes ponerle una en ese momento, ponle una fecha para volver a revisar la tarea, pero nunca dejes nada sin que este establecido una tiempo para ser atendido.

  • No existe un objetivo claro. Esto provoca que no se sepa que es lo que hay que hacer y por lo tanto no se hada nada relevante, agregando módulos al sistema o quitándolos sin sentido alguno. Es necesario tener unos objetivos definidos, cerrados y limitados.

  • No existen unas tareas claras. Dicho de otra forma se tiene un objetivo, pero no se ha analizado claramente y no se sabe cómo llegar a él, con lo que se va dando tumbos, realizando código (u otras tareas), sin saber muy bien como llega a buen puerto. Es necesario trazar una ruta para poder conseguir nuestros objetivos, dividir el problema en pasos y recorrerlos uno a uno.

  • No hacer una tarea por estar esperando un correo o una llamada. No hagas eso, nunca van a llegar en el tiempo adecuado y el resultado es que no vas a hacer nada. Finalmente el trabajo de las personas no es responder correos, así que no estarán pendientes para contestarte o lo harán cuando acaben sus tareas, o incluso si son bien organizados tendrán un día, o una hora al específica para contestar todos los correos. Mi recomendación es que mandes un correo, y después llames por teléfono para avisar que mandaste cierta información por correo. Sobretodo que establezcas un tiempo prudencial determinado y concreto para obtener respuesta a ese correo, y que además la persona receptora sea consciente de dicho tiempo, si no se ha cumplido vuelve a llamarla. Es mucho mejor ser molesto, que no hacer tu trabajo.

  • Diferentes áreas, diferentes intereses. Este problema es muy común, dos áreas involucradas tienen responsabilidades diferentes con respecto al proyecto, esto es un problema de difícil solución y tiene que ver con que no se percibe que un sistema incompleto no le sirve a ninguna de las dos áreas (es del famoso caso de "No es el lado de mi barco el que se hunde"), es necesario aumentar la comunicación entre áreas, para llegar a un mutuo acuerdo, a veces solo es un mero problema de comunicación.


  • Plantear un problema sin la solución. Es necesario que cuando tengamos un problema, intentemos buscar una solución antes de delegárselo a otra área. Es importante presentar los problemas junto con las soluciones

  • Para cada solución tiene un problema o más. Es un caso curioso, cuando se dan soluciones a un problema, inmediatamente surgen nuevos problemas que invalidan la propuesta, de forma que al final ninguna acción es tomada, nada se hace. Hay que considerar que ninguna solución resuelve completamente un problema, pero es mejor tener una solución que haga algo a no tener nada en producción.

Ahora bien, ¿Puedes tomar decisiones desde cualquier puesto? Si, por que si realmente no tuvieras que tomar decisiones en tu puesto, sería más fácil reemplazarte con algún tipo de proceso automatizado, si no es el caso, es que tus decisiones afectan a tu trabajo y a la forma de desarrollarse de este, y puedes influir en el éxito del proyecto.