miércoles, 14 de noviembre de 2018

Principio de Inversión de Dependencia

Es un principio estructural, que habla que los módulos de alto nivel no deben depender de los módulos de bajo nivel, en otras palabras las dependencias del código se refieren solo a abstracciones, no a concreciones. El seguir este principio nos da el control sobre la dirección de todas las dependencias.

Para ilustrar lo anterior miremos la siguiente imagen.


En esta imagen vemos dos componentes uno es el de las vistas y otro es el de los controladores y como se observa un controlador necesita una instancia de la vista para poder indicar que acción realizar, pero esto presenta varios problemas, por ejemplo, no queremos que un cambio en la Vista1 nos obligue a volver a compilar o modificar nuestra clase Controlador1, para esto podemos usar la inversión de dependencia como veremos en la siguiente imagen.


Al crear una interfaz en el componente de los controladores que abstrae el comportamiento de una vista podemos invertir la dependencia, ahora el componente de las vistas depende del componente de los controladores, de esta forma protegemos al componente de los controladores de los cambios en componente de las vistas.

Que tal si vemos un ejemplo más concreto para ver esto.

Imaginemos estamos viendo el espectáculo de Gepetto y Pinocho y decidimos recrearlo en una pequeña aplicación.



Para empezar con nuestra aplicación debemos hacernos un par de preguntas.
¿Quien conoce como manipular un títere?, ¿Quien realiza la acción?. Es claro que quien sabe como manipular un títere es Gepetto y quien realiza la acción es Pinocho, solo con estas dos simples preguntas ya podemos hacernos una idea quien tiene las funciones de alto nivel y quien las de bajo nivel.

Gepetto contiene las funciones de alto nivel (Es quien conoce las reglas del negocio y sabe como manipular un titere), cuales serian estas funciones.

  • void mostrarTitereInmovil();
  • void moverCabezaDelTitere();
  • void moverBrazoIzquierdoDelTitere();
  • void moverBrazoDerechoDelTitere();
  • void moverPiernaIzquierdoDelTitere();
  • void moverPiernaDerechoDelTitere();

Pinocho contiene las funciones de bajo nivel (Es quien simplemente realiza las acciones que Gepetto sabiamente le ordena), cuales serian estas funciones.

  • void inmovil();
  • void moverCabeza();
  • void moverBrazoIzquierdo();
  • void moverBrazoDerecho();
  • void moverPiernaIzquierdo();
  • void moverPiernaDerecho();


Para ilustrar esto tendremos dos componentes uno de alto nivel (donde estará Gepetto) y otra de bajo nivel (Donde estará Pinocho). A estas alturas es obvio que Gepetto debe tener una instancia de Pinocho para poder manipularlo por lo que el diagrama se vería algo así.


Es evidente a simple vista que el componente de alto nivel depende del componente de bajo nivel lo cual es un error, imaginemos que Pinocho sale a vacaciones y viene en su reemplazo Pepito Grillo, ¿Qué pasaría?, ¿Tendríamos que hacer cambios en nuestra aplicación?, es obvio que la respuesta es si, primero Gepetto no debería estar condicionado a trabajar con un solo títere, él es un titiritero y sabe manipular a cualquier títere que le pongan, por lo cual empiezo a evidenciar un problema de abstracción, una ultima consideración seria, y que tal que él que salga a vacaciones es Gepetto, podríamos crear un nuevo titiritero para Pinocho, es evidente que con el diseño actual no podríamos, por lo que vamos a hacer una mejor abstracción y mejorar considerablemente nuestro diseño e invirtiendo la dependencia.

Creo que ya nos dimos cuenta que los conceptos principales del dominio de la aplicación son Titiritero y Títere por lo que creo que deberíamos abstraer su comportamiento creando dos Interfaces y que estas contengan la declaración de las funciones ya mencionadas.

Como segundo paso Gepetto debería implementar la interfaz Titiritero porque es claro que él es un titiritero.

El tercer paso consiste en que Pinocho implemente la interfaz Titere.

Ahora Gepetto no debe tener una instancia de Pinocho sino declara una variable de clase de tipo Titere y es necesario entonces que los titiriteros tengan una función adicional setTitere(Titere titere) tengamos presente que en un espectáculo de títeres un titiritero puede cambiar contantemente de títere y creo que esta función le caería de mil maravillas.

Por ultimo viene la pregunta crucial donde deben ir estas interfaces porque de dicha ubicación dependen si se logra la inversión de dependencia o no.  Imagino que ya tienes la respuesta, si así es, deben ir en el componente de alto nivel, nuestro nuevo diagrama queda así.


Si comparas los dos diagramas ahora la flecha de dependencia se ha invertido y el componente de bajo nivel depende del componente de alto nivel y nuestra aplicación ahora puede crecer de una forma mas flexible, puedes crear más títeres y mas titiriteros si así lo deseas.

El código fuente de este ejemplo en encuentras en mi repositorio de GitHub dedicado a la Programación Orientada a Objetos

Si deseas ampliar más este tema te recomiendo el artículo The Dependency Inversion Principle de Robert C. Martin.

Si deseas leer sobre los demás principios SOLID da click aquí.

Principio de Segregación de la Interfaz

Este es un principio estructural y para definirlo utilizare la conclusión dada por Robert C. Martin en su libro Arquitectura limpia mas un par de mis palabras, entonces, este principio se ocupa de las desventajas de depender de algo que carga con un equipaje que no necesita, ya que, depender de esto puede traerle problemas que no espera.

En algunos otros textos se hablara de depender de interfaces "gordas" (Interfaces que declara más métodos de los que necesita una clase) y estas clases que tienen interfaces "gordas" son clases cuyas interfaces no son cohesivas, por lo que son forzadas a depender de interfaces que no utilizan. Cuando los clientes se ven obligados a depender de interfaces que no usan, esos clientes están sujetos a cambios en esas interfaces, esto resulta en un acoplamiento inadvertido entre todos los clientes.

Ilustremos lo anterior con un ejemplo, imaginemos que tenemos una clase llamada OperacionesMatematicas con 4 métodos (sumar, restar, multiplicar y dividir), esta clase es empleada por 4 clientes y cada uno utiliza solo uno de los métodos. Como ven si estamos empleando un lenguaje de programación como Java cada cliente se vería obligado a depender los demás métodos, lo anterior lo vemos en la imagen.

La solución al problema anterior lo vemos en la siguiente imagen, donde se segregan las operaciones en interfaces.

Si deseas ampliar más este tema te recomiendo el artículo The Interface Segregation Principle de Robert C. Martin.

Si deseas leer sobre los demás principios SOLID da click aquí.

Programación Orientada a Objetos (POO) desde un Enfoque Simple

Este paradigma es descubierto en el año 1966, por Ole Johan Dahl y Kristen Nygaard, dos años antes que la programación estructurada, pero es adoptado después de esta.

Dentro de la POO existen dos conceptos claves.
  • Clase: Modelo o plantilla a partir del cual podemos crear los objetos.
  • Objeto: Un objeto es una unidad de software de estado y comportamiento relacionados, también puede describirse como la instancia de una clase.
Profundicemos un poco en los objetos, estos a menudo se utilizan para modelar los objetos del mundo real, esto incluye conceptos, cosas tangibles e intangibles que se encuentran en la vida cotidiana. Los objetos del mundo real comparten dos características: 
  • Estado 
  • Comportamiento
Por ejemplo, los perros tienen estado (nombre, color, raza, hambre) y comportamiento (ladrar, buscar, menear la cola). Un objeto almacena su estado en campos, atributos o variables  y expone su comportamiento a través de métodos o funciones, los métodos operan el estado interno de un objeto, pero para que dicho objeto o una clase realice alguna acción se debe enviar un mensaje, pero no basta con esto, ya que, para que el objeto o la clase procese el mensaje que recibe, debe poseer un método de coincidencia. Ilustremos lo anterior con un ejemplo escrito en Java.

Automovil auto = new Automovil();
auto.encender();

Como podemos ver, creamos a partir de la clase Automovil un objeto llamado auto al cual le enviamos el mensaje auto.encender() ya que queremos que encienda, pero para que esto se de la clase Automovil debe tener un método que coincida con encender()

Teniendo claro y comprendiendo los conceptos de Clase y Objeto, podemos hablar del Diseño Orientado a Objetos (DOO), que como menciona Rober C Martin en su libro Arquitectura limpia, "podemos utilizar  tres palabras mágicas para explicar la naturaleza del DOO: encapsulación, herencia y polimorfismo. La implicación es que el DOO es la combinación adecuada de estas tres cosas"

Encapsulación: Describe la capacidad de un objeto para ocultar sus datos y métodos del resto del mundo. Ocultar el estado interno y exigir que toda la interacción se realice a través de los métodos de un objeto se conoce como encapsulación de datos. Vemos la encapsulación con las variables privadas y métodos publicos de una clase.

Herencia: Diferentes tipos de objetos a menudo tienen una cierta cantidad de características en común entre sí, sin embargo, cada uno también define características adicionales que los hacen diferentes. La programación orientada a objetos permite que las clases hereden el estado y el comportamiento de uso común de otras clases. De aquí que Rober C Martin en su libro Arquitectura limpia la defina como "La redeclaración de un grupo de variables y funciones dentro de un ámbito de inclusión".

Polimorfismo: Es la capacidad de diferentes objetos para responder de manera diferente al mismo mensaje. Ejemplo: Tenemos dos clase EstudiantePregrado y EstudiantePosgrado que Heredan de la clase Estudiante, el polimorfismo nos permitiría que una variable de tipo Estudiante no se limite a referirse a un objeto de la clase EstudiantePregrado, ya que, puede hacer referencia a cualquier objeto de las clases descendientes de Estudiante.

Para finalizar hablemos de la Abstracción una palabra muy utilizada cuando hablamos de POO pero que es?, bueno se podría decir que la abstracción consiste en captar las características esenciales de un objeto, así como su comportamiento.

En mi repositorio de GitHub https://github.com/cedaniel200/Programacion-Orientada-a-Objetos encontraras algunos talleres donde puedes practicar los temas vistos y más, en caso de no poder solucionarlos, no te preocupes cada taller tiene una posible solución.

martes, 13 de noviembre de 2018

Principio de Sustitución de Liskov

En la siguiente cita Barbara Liskov definía los subtipos:
Lo que se busca aquí es algo parecido a la siguiente propiedad de sustitución: si por cada objeto o1 del tipo S hay un objeto o2 del tipo T, como aquel para todos los programas P definidos en términos de T, el comportamiento de P no cambia cuando o1 es sustituido por o2, por lo que S es un subtipo de T.  Barbara liskov, "Data Abstraction and Hierarchy", SIGPLAN Notices 23, 5 (Mayo 1988).

Esto es lo que se conoce como Principio de Sustitución de Liskov, el cual hace parte de los principios SOLID y podemos llevarlo a otras palabras no tan formales, más simples y coloquiales, por lo que se podría decir que:

Las clases Base deben poder usar objetos de clases derivadas sin conocerlos. Es decir los tipos derivados son completamente sustituibles por sus tipos base.

En otras palabras este principio afirma que, para crear sistemas de software a partir de partes intercambiables, esas partes deben adherirse a un contrato que les permita ser sustituidas por otras.

Veamos un ejemplo para poder entender mejor esto.


Imaginemos que tenemos una clase Motor como se muestra en la imagen anterior, esta clase tiene un método prender() y dos subtipos Motor2Tiempos y Motor4Tiempos y cada uno tiene su forma de prender. Por su parte la clase Motocicleta invoca el método prender() independiente de cual de los dos subtipo utilice (Ambos subtipos son sustituibles por el tipo Motor) lo que garantiza que se esta cumpliendo el principio de sustitución de Liskov. 

Espero este sencillo ejemplo les permita entender de una forma simple este principio.

Si deseas ampliar más este tema te recomiendo el artículo The Liskov Substitution Principle de Robert C. Martin.

lunes, 10 de abril de 2017

Clean Code Capítulo 5 Formato

Este capítulo se resumen en la necesidad que debemos tener de preocuparnos por el formato de nuestro código. Por lo anterior y si se trabaja en equipo el autor expresa que se “debe acordar una serie de reglas que todos los miembros deben cumplir”.

 Como la comunicación debe ser el pilar de un desarrollador profesional, el formato son esas reglas que utilizamos al escribir nuestro código, permitiendo comunicarnos mejor. El libro Clean Code nos menciona una serie de formatos o reglas a seguir.

 Formato Vertical: se refiere al tamaño o número de líneas que debe tener un archivo fuente, esto lo quiero resumir diciendo que no hay un tamaño vertical máximo, pero que un archivo pequeño es más fácil de entender que uno muy extenso, en este punto quiero mencionar la metáfora del periódico descrita en el libro:

 “Piense en un artículo de periódico bien escrito. En la parte superior espera un titular que indique de què se trata la historia y le permita determinar si quiere leerlo o no. El primer párrafo ofrece una sinopsis de la historia, oculta los detalles y muestra conceptos generales. Al avanzar la lectura, aumenta los detalles junto con toda las fechas, nombres, citas y otros elementos.

 Un archivo de código debe ser como un artículo de periódico. El nombre debe ser sencillo pero claro. Por sí mismo, debe bastar para indicarnos si estamos o no en el módulo correcto. Los elementos superiores del archivo deben proporcionar conceptos y algoritmos de nivel superior. Los detalles deben aumentar según avanzamos, hasta que en la parte final encontremos las funciones de nivel inferior del archivo.

 Un periódico se compone de varios artículos, algunos muy reducidos y otros de gran tamaño. No hay muchos que ocupen toda la página con texto, para que el periódico sea manejable. Si el periódico fuera un único y extenso texto con una aglomeración desorganizada de hechos, fechas y nombres, no lo leerìamos.”

 Apertura Vertical entre Conceptos: Por lo general colocamos una línea tras otra, las cuales  representan un concepto, estos conceptos se deben separar mediante líneas en blanco.

 package cedaniel200.com.ejemplo;

public class Ejemplo {

        private static final String AUTOR = “cedaniel200”;
        private String mensaje;

         public void setMensaje(String mensaje){
                 this.mensaje = mensaje;
        }

        public void imprimirMensaje(){
                System.out.println(AUTOR + ”: ” + this.mensaje);
        }

 }

 Distancia Vertical: Lo conceptos relacionados deben estar juntos verticalmente, es decir, uno seguido del otro.

 Declaración de Variables: Se aconseja declararlas cerca a su uso, así, las variables locales se debe declara al comienzo de cada método, las variables de control de bucles se deben declarar dentro de este, como hay excepciones, por ejemplo cuando la declaración es muy larga, esta se debe hacer en la parte superior de un bloque o antes del bucle.

Variables de Instancia: Deben declararse en la parte superior de cada clase.

 Funciones Dependientes: Si una función llama a otra estas deben estar lo más cerca posible, es decir, la función que invoca debe ir primero seguida de la función invocada.

 Afinidad Conceptual: Este punto se refiere a ubicar lo más próximo posible los conceptos similares, por ejemplo, existe una clase que tiene varias funciones que no se invocan entre sí, pero varias funciones realizan operaciones similares, estas deben estar próximas la una de la otra, es decir, una seguida de la otra.

 Formato Horizontal: trata de la cantidad de caracteres que debe tener una línea, el autor recomienda 120 caracteres como límite. Lo que sí es cierto es que tener que utilizar el scroll horizontal para ver toda la línea es algo molesto y que dificulta la legibilidad, entonces mi recomendación es que el tamaño de la línea no exceda al de la pantalla ocasionando la necesidad de utilizar es scroll horizontal.

 Apertura y Densidad Horizontal: Trata de la utilización de los espacios en blanco horizontales para destacar las partes que componen una línea, por ejemplo en una asignación se utiliza para acentuar la separación de sus dos partes:
 int numeroMaximoAlumnos  =  12;

 también se utiliza para separar argumentos en un método:

public void imprimirMensaje(String autor,  String mensaje){
        // implementación del método

 Separar los argumentos al invocar un método:

 imprimirMensaje(“cedaniel200”, “Hola Mundo”);

 También se utilizan los espacios para acentuar precedencia de los operadores:

  int resultado = c*c - 5*c + 3*c*b;

 Sangrado: Se debe sangrar cada línea dependiendo de su nivel, un sangrado por cada nivel desde el segundo, por ejemplo:

public class Persona { // Primer nivel por lo tanto no se sangra

        private String nombre; // Segundo nivel por eso tiene un sangrado
        public void setNombre(String nombre){
                this.nombre = nombre; // Tercer nivel por eso tiene dos sangrados
        } 
}

Nota: Para más artículos relacionados ver Clean Code.

martes, 24 de enero de 2017

Principio de Responsabilidad Única


Este principio es clave en la programación orientada a objetos, y nos dice que cada clase debe tener un solo motivo para cambiar, entendiendo como motivo la responsabilidad que tiene la clase, en otras palabras, para qué fue hecha o qué hace en específico. También se le puede llamar cohesión, entendiendo cohesión como la medida en que un componente (clase, paquete, método) se dedica a realizar solo la tarea para la cual fue creado.

¿Por qué es necesario aplicar este principio? Bueno, partamos de que tenemos una clase A que tiene dos responsabilidades, es decir, hace dos cosas; de ahora en adelante las llamaremos R1 y R2. En el cuerpo de la clase, esta tiene varios métodos, donde algunos de estos se utilizan tanto para llevar a cabo R1 como para R2. Al momento de la creación de la clase no hay ningún problema y todo funciona bien.

Un mes después ...

Es hora de mantener nuestra aplicación, debemos cambiar una clase. Espero hayas adivinado qué clase es, ¡sí, es la clase A!, resulta que R1 no está del todo bien y se debe corregir, entonces, empieza nuestra labor.

Tres días después, 500 tazas de café y pocas horas de sueño después ...

Creemos que hemos terminado y cuando se empiezan a realizar las pruebas... OOOOOH sorpresa, tú o el equipo de testing (o como se le llame en tu empresa) se dan cuenta que ahora R1 funciona bien, pero R2 no funciona. ¿Qué pasó?, recordemos algo: “Algunos de los métodos de la clase A se utilizan por R1 y R2”, entonces al modificar alguno de estos métodos, se corrige R1 pero genera errores en R2.

Es por lo anterior que se debería cumplir este principio, así cuando se haga un cambio tendremos la certeza que solo se afectará una responsabilidad. Otra justificación se hace evidente cuando ocurre un error y se detecta qué es, es fácil identificar la clase que tiene esa responsabilidad.

Si deseas ampliar más este tema te recomiendo el artículo The Single Responsibility Principle de Robert C. Martin.

Si deseas leer sobre los demás principios SOLID da click aquí.

miércoles, 18 de enero de 2017

Qué Método de Estimación Utilizar para mi Sprint?


Este artículo surge de un comentario que hice al artículo Planear sprints en horas y no por puntos de Lucho Salazar.

Estimar, según la RAE es “Calcular o determinar el valor de algo” o “Creer o considerar algo a partir de los datos que se tienen”, y como todos, en nuestra poca o mucha experiencia, sabemos que estimar no es algo trivial y aún más cuando estamos empezando en este mundo; ahora, con el paso del tiempo mezclamos las dos definiciones, ya que, calculamos con base en datos obtenidos con el paso de cada sprint; o al menos ese es el deseo, o, a mi consideración, lo que se debería hacer.

¿Qué método de estimación utilizar para planear mi sprint?, es la pregunta que muy seguramente nos surge cuando vamos a empezar, y según a quién se la formules te dará una respuesta con la cual, probablemente, muchos coinciden, debido a que unos métodos son más populares que otros.

Para responder esta pregunta, yo diría que todo método de estimación puede llegar a ser válido para un equipo, siempre y cuando este adquiera la madurez y la destreza en su utilización. Esto se consigue inicialmente a prueba y error, pasando por el punto en que se aprecia el avance en la utilización del método escogido, hasta conseguir dominarlo; en caso de no conseguir ningún avance es mejor cambiar y experimentar con otro método.

Para justificar mi postura, partamos de un método cualquiera (sea por horas, por puntos o puntos que equivalen tiempo o esfuerzo, u otro que hayas escuchado) y mi consejo es que te documentes muy bien al respecto antes de empezar. Comenzamos planificando los primeros sprint, los cual tenderán a errores (sobre o bajo la línea del Burndown) debido a la incertidumbre que conlleva iniciar con algo que, aunque esté documentado, se desconoce su comportamiento en la práctica; es con el tiempo (sprint tras sprint) en que este margen de error baja a su mínima expresión. Hay que tener en cuenta que se pueden obtener estadísticas como la velocidad promedio y las que creamos necesarias, con las cuales se puede predecir la estimación con base en datos y cada vez ser más acertados al planear el sprint.

Por lo anterior, estoy de acuerdo con Lucho cuando en su artículo expresa que "igual que con los puntos, estimar con horas no nos eximirá del error inherente a las estimaciones", para acuñar esta frase a este artículo he realizado unos pequeños cambios, quedando de la siguiente forma:

"estimar, independientemente del método utilizado, no nos eximirá del error inherente a las estimaciones"

Independientemente del método, la experiencia en su utilización te ayudará a obtener buenos resultados. Experimentar es fundamental para esto, puede que un método de estimación te dé mejores resultados que otro, y por lo tanto, sea el momento de realizar un cambio, ya sea de puntos a horas o de horas a puntos, o quizás algún método distinto a los aquí mencionados.

Instalación NodeJS

Ingresamos a la página oficial de NodeJS donde lo descargaremos  https://nodejs.org/en/download/ Escogemos el instalador que se ajuste a ...