¡Hola! ¿Cómo están?. Gracias por volver a este su blog de código, esta vez les presento el comienzo de una serie de posts acerca de Patrones de Diseño. En este primer post definiremos que es un Patrón de Diseño y cuáles son las herramientas y conocimientos básicos para su construcción. Esta serie de posts está basada en el Libro Head First Design Patterns, Primera Edición - ISBN: 978-0-596-00712-6

Considero que es un muy buen libro de referencia, fácil de leer y de entender. Me considero una persona muy visual, tiendo a recordar más las cosas que veo que las que escucho o siento y este libro me ha dado una gran ayuda para visualizar mejor lo que entiendo. Este libro me lo recomendó el Ing. Juan Antonio alias @juanitodread, a quien considero un Software Developer muy bueno, bastante completo y apasionado por esto del software, a quien le he aprendido mucho. Les hago mención de esta persona y se los presento formalmente como colaborador de este blog y de esta serie de posts.
¡Vámos a Comenzar!


El código elaborado para esta serie de posts se encontrará en: Repositorio GitHub - Design Patterns


Patrón de Diseño

Un patrón de diseño es una técnica que ha sido elaborada, probada y validada para resolver un problema en específico, no es código, sino la solución para resolver el problema. La solución al problema se da en base a un comportamiento que es repetitivo (por lo general de cambio) lo cuál genera un patrón en la forma de solucionar el problema, lo que deduce que los patrones no fueron inventados, sino descubiertos. Se conoce como patrón de diseño porque la problemática se presenta (y debe ser atendida) en el momento en que estamos diseñando nuestro software. En esta etapa del desarrollo de software es donde surge la problemática de Cómo solucionarlo y Con qué y es donde se puede aplicar un patrón que enseñe como construir sistemas con las mejores cualidades del Diseño OO.
Como dijimos antes, los patrones son técnicas probadas y validadas que se obtuvieron a través de observar un mismo comportamiento en problemas de diseño de software y que se solucionaron dando nacimiento a un patrón que resolvió esos problemas y los cuales podemos aplicar a nuestro software. Ya existe alguien que resolvió nuestros problemas de diseño de software, ¡HURRA!.

Motivación


Así es como tu cerebro quedará con el conocimiento de patrones de diseño.

Conceptos Básicos de POO

Los conceptos básicos de POO que se utilizan para la creación de Patrones de Diseño son: Abstracción, Encampsulamiento, Herencia y Polimorfismo. Estos conceptos los puede ver en POO en Java (Conceptos).

Principios de Diseño

Veamos los principios de diseño como consejos a seguir en la elaboración de un Patrón de Diseño. Los principios de diseño son como una guía que si la seguimos no solo lograremos resolver nuestro problema con un Patrón, sino que mejoraremos nuestra forma de programar orientado a objetos, recuerde que los patrones de diseño no fueron creados, sino descubiertos y no nos proporcionan código sólo soluciones generales para resolver problemas. A lo largo de la serie de Posts iremos presentando nuevos principios de diseño, por lo pronto los siguientes son los básicos para lo que sigue.
  • Identifica los aspectos en nuestra aplicación que varían y separarlos de los que se mantienen igual. Esto es, tomar las partes que varían y encapsularlas, entonces después podemos alterar o extender las partes que varían sin afectar las que no lo hacen.
  • Programa una interfaz, no una implementación. Se refiere a programar un medio   que permita la conexión con otras clases sin tener que implementarlo.
  • Favorece la composición sobre la herencia. 

En el siguiente post veremos como aplicar los conceptos básicos de POO y los principios de diseño que hemos visto en un patrón de diseño.

Aquí la lista de los patrones de diseño que se han incluido en este blog:

Clase: Una clase describe las características de un conjunto de objetos mediante atributos y operaciones. Representa un Tipo. En Java el nombre de la clase debe ser igual al nombre del archivo en que la estamos guardando.

public class Cuenta { . . .
Cuenta.java
  • Atributo (variables miembro, variables clase): Describen el estado interno de cada objeto.
  • Operación (métodos, funciones miembro): Describen lo que se puede hacer con el objeto y los servicios que proporciona.

Objeto y composición: Un objeto es una Instancia de una clase. Representación de la clase con sus características propias. En Java la creación de objetos se hace de forma dinámica mediante la declaración de referencias de clase conectadas a la creación de objetos con el operador new que manda llamar al constructor adecuado. Es una buena práctica sobrecargar el constructor por default (vacío) para así poder crear un objeto con mínimo 2 formas distintas, una vacía y otra con la inicialización de todos los atributos o los más representativos.

Cuenta cuenta; //Creación de Referencia
cuenta = new Cuenta(); //Conectar referencia con objeto nuevo


La composición es la relación que existe entre clases mediante la referencia de objetos de una clase como miembros de otra. En java esto se representa haciendo una referencia a otra clase como parte de sus atributos viéndolo así como una relación de tiene un.

public class Cuenta{
  private long numero;
  private Movimiento movimiento;

  public Cuenta(long numero, Movimiento movimiento){ ... }
  . 
  .
  public Movimiento getMovimiento(){ ... }
  public void setMovimiento(){. . .

Encapsulamiento y Principio de Ocultación: Se refiere a la recolección de características que pueden pertenecer a una misma clase, y que están al mismo nivel de abstracción, las cuales serán ocultadas para la protección del estado interno de un objeto. En Java el principio de ocultación es representado por los Modificadores de Acceso public, private, protected. Una buena práctica es ocultar (con el modificador de acceso private) los atributos de una clase para que sólo puedan ser vistos por los miembros de la clase interna, y dejar el acceso de obtención y modificación a los Getters y Setters.

public class Cliente{
  private String nombre;
  . 
  .
  public String getNombre(){ ... }
  public void setNombre(){. . .

Herencia: Mecanismo por el cuál es posible crear una clase incorporando de manera implícita todas las características de una clase existente. Al igual que en la vida real vemos reflejada la herencia como una relación entre objetos clasificándolos de forma jerárquica por la generalización y especificación, para evitar redundancias en un mismo contexto y para reutilizar características, entre otros. 
En Java se hereda de una clase a otra con la palabra extends y todas las clases en Java heredan de la clase Object siguiendo así el paradigma al mismo lenguaje. Java provee dos tipos de abstracciones de clase que facilitan la herencia, las clases Abstractas (abstract) que son clases abstractas que brindan características especiales con carencia de implementación y que no requieren ser instanciadas por su misma naturaleza abstracta,  y las interfaces (interface) que son clases completamente abstractas que no tienen implementación, únicamente brindan estructura (operaciones, atributos static final) y si pueden ser instanciadas a diferencia de la clase abstracta, además de brindar una simulación de "herencia múltiple restringida" ya que en Java la herencia múltiple no está permitida por evidentes desventajas como la ambigüedad, mal diseño, mala eficiencia etc. Java promueve la creación de Interfaces por su sencillez.


//Herencia de una clase
public class CuentaAhorroFijo extends Cuenta{. . . 

//Herencia de una clase abstracta
public abstract class Cuenta{. . .
public class CuentaAhorroFijo extends Cuenta{. . .

//Herencia de una clase abstracta y una interfaz
public abstract class Cuenta{. . .
public interface Movimiento{. . .
public class CuentaAhorroFijo extends Cuenta, implements Movimiento{. . .

Polimorfismo (upcasting):  El polimorfismo ayudado de la herencia es la posibilidad de que una referencia a objetos de una clase pueda conectarse con objetos de descendientes de esa clase. El polimorfismo nos permite hacer una generalización buscando puntos en común a todos los objetos en un ancestro. En Java se puede implementar el polimorfismo de diferentes formas, ya sea como la asignación de una referencia hija a una padre, como el paso de parámetros a una operación que recibe parámetros de múltiples clases, permitiendo la creación de estructuras de datos que soporten objetos de distintas clases, entre otras. Se debe tomar en cuenta que al hacer polimorfismo el objeto polimórfico solo puede hacer referencia a las operaciones del objeto al que se ha conectado. También es posible hacer polimorfismo con las interfaces de java.

//Asignación ordinaria
A ra = new A();
//Asignación polimorfa
B rb = ra;
//Asignación polimorfa
B rb = new A();

Java resuelve las llamadas a funciones que suelen implementarse con objetos polimórficos, en tiempo de ejecución de manera dinámica, para garantizar siempre la llamada correcta a una función, a esto se le conoce como ligadura dinámica. 



La operación contraria al upcasting es el downcasting, y es cuando solemos necesitar recuperar el objeto original después de haber hecho una conexión polimorfa. Lo que quiere decir que en vez de hacer una generalización lo que hacemos es una especialización, sin embargo no se puede hacer un downcasting simplemente conectando una referencia como en el upcasting, para esto es necesario hacer un simple casting que especifique la clase original.

B rb = new A(); //upcasting
A ra = (A) rb;  //downcasting

Cohesión: Medida de relación funcional que existe entre las operaciones de una clase. Una operación cohesionada ejecutará una única tarea sencilla y relacionándose poco o nada con las demás operaciones de la clase. Una buena práctica es hacer operaciones altamente cohesivas, para que las operaciones sean independientes unas de otras y al momento de hacer modificaciones a una operación el cambio afecte al menor número implementaciones posibles, evitando los errores y mejorando el diseño de estructuras.

Acoplamiento: Grado de interdependencia que existe entre clases, refiriéndose a las relaciones dependientes que puede haber entre clases. Una buena práctica es tener un bajo acoplamiento para que al surgir una modificación el cambio afecte al menor número de implementaciones posibles. Esto disminuye errores y mejora el diseño de estructuras.

Categories

Seguidores

MarceStarlet. Con la tecnología de Blogger.
Powered By Blogger