Tabla de contenidos

Sobre esta publicación

Esta publicación se distribuye como un e-book gratuito y se encuentra en constante evolución. Puede acceder a la versión más actualizada desde la siguiente dirección:

https://www.designpatternsingo.com/

Los códigos fuentes de ejemplo de los patrones de diseño utilizados en este e-book se encuentran en el siguiente repositorio público:

https://github.com/danielspk/designpatternsingo

Asimismo, el código fuente de este e-book se encuentra en el siguiente repositorio público:

https://github.com/danielspk/designpatternsingoebook

Última actualización: 15 de Mayo de 2020 a las 13:20 hs.

Introducción

Este trabajo tiene como objetivo discutir sobre la programación orientada a objetos en el lenguaje de programación Go, y como pueden implementarse los 23 Patrones de Diseño GoF.

Organización

La primera parte del trabajo está destinada a la presentación del lenguaje Go y a como se puede aplicar el paradigma orientado a objetos.

En la segunda parte se expone como pueden implementarse los 23 patrones de diseño GoF.

La tercera y última parte está destinada a las conclusiones generales y a los motivos que me llevaron a realizar esta publicación.

Imagen - [52]

Parte I

Presentación del Lenguaje Go y sus características Orientadas a Objetos

Sobre Go

Imagen - [49]

Go es un lenguaje de programación de código abierto que facilita la creación de software simple, confiable y eficiente”. [1]

Go es expresivo, conciso, limpio y eficiente. Sus mecanismos de concurrencia facilitan la escritura de programas que aprovechan al máximo las máquinas multinúcleo, y de red, mientras que su novedoso sistema de tipo permite la construcción de programas flexibles y modulares. Go compila rápidamente el código de máquina y tiene la comodidad de la recolección de basura y el poder de la reflexión en tiempo de ejecución. Es un lenguaje compilado, rápido, de tipado estático, que se siente como un lenguaje interpretado de tipado dinámico”. [2]

Orígenes

Go fue creado en Google en el año 2007 por Robert Griesemer, Rob Pike, y Ken Thomson.

Imagen - [50]

Su lanzamiento oficial fue en noviembre del año 2009, pero su primera versión estable - 1.0 - recién se publicó en marzo de 2012.

Originalmente fue concebido para resolver problemas propios de la infraestructura de software de Google. Según palabras de unos de sus creadores Rob Pike, “Los objetivos del proyecto Go fueron eliminar la lentitud y la torpeza del desarrollo de software en Google y, por lo tanto, hacer que el proceso sea más productivo y escalable. El lenguaje fue diseñado por y para las personas que escriben, leen, depuran y mantienen sistemas de software grandes. Por lo tanto, el propósito de Go no fue investigar el diseño de un lenguaje de programación; sino mejorar el entorno de trabajo para sus diseñadores y sus compañeros de trabajo. Go tiene más que ver con la ingeniería del software que con la investigación en un lenguaje de programación” [17].

Entre los principales problemas de Google que motivaron el desarrollo de Go se pueden destacan:

Sobre su nombre

Dado que la palabra Go es parte del idioma ingles el lenguaje también es conocido como Golang.

Características

Go está inspirado en la sintaxis de C como otros lenguajes: C++, C#, Java, PHP, Javascript, etc.

Su elección fue ser afín a la gran comunidad de desarrolladores de C++ de Google.

Por sus características suele clasificarse como un lenguaje compilado que tiene características de lenguajes interpretados.

Imagen - [25]

Para Rob Pike: “Go es un intento de combinar la seguridad y el rendimiento de un lenguaje de tipado estático con la expresividad y la comodidad de un lenguaje interpretado de tipo dinámico.” [25]

Go se caracteriza por ser un lenguaje:

Se destaca también por su diseño minimalista y su facilidad para aprenderlo. A modo de comparación mientras otros lenguajes tienen muchas palabras reservadas, C++ 20 tiene 96, C# 7 tiene 78, Java 13 tiene 51; Go solo tiene 25.

Ejemplo

1 package main
2 
3 import "fmt"
4 
5 func main() {
6     fmt.Println("www.designpatternsingo.com")
7 }

Ejecutar código: https://play.golang.org/p/vhgR-fZxZv6

Según otros Autores

Para Mark Summerfield [36]Go es bastante parecido a C en su espíritu, ya que es un lenguaje pequeño y eficiente con convenientes facilidades de bajo nivel, como punteros. Sin embargo, Go también ofrece muchas características asociadas con lenguajes de alto o muy alto nivel, como cadenas Unicode, potentes estructuras de datos integradas, duck typing, recolección de basura y soporte de concurrencia de alto nivel que utiliza comunicaciones en lugar de datos compartidos y bloqueos. Go también tiene una gran biblioteca estándar de amplio rango”.

Para Shiju Varghese [28] “El lenguaje de programación Go se puede describir simplemente en tres palabras: simple, mínimo y pragmático. El objetivo del diseño de Go es ser un lenguaje de programación simple, minimalista y expresivo que proporcione todas las características esenciales para crear sistemas de software confiables y eficientes. Cada idioma tiene su propio objetivo de diseño y una filosofía única. La simplicidad no se puede agregar más adelante en el idioma, por lo que debe ser construida con la simplicidad en mente. Go está diseñado para ser simple. Al combinar la simplicidad y el pragmatismo de Go, puede construir sistemas de software altamente eficientes con un mayor nivel de productividad”.

Para Karl Seguin [20]Go tiene la naturaleza de simplificar la complejidad que hemos visto incluida en los lenguajes de programación en el último par de décadas mediante el uso de varios mecanismos”.

Para Caleb Doxsey [41]Go es un lenguaje de programación de propósito general con características avanzadas y una sintaxis limpia. Debido a su amplia disponibilidad en una variedad de plataformas, su robusta biblioteca standard bien documentada y su enfoque en buenos principios de la ingeniería del software, Go es un gran lenguaje de programación para aprender”.

Controversias

Go como todos los lenguajes de programación presenta ciertas controversias. Sus detractores por ejemplo manifiestan que el lenguaje carece de:

No obstante el equipo de diseño de Go no es ajeno a estas críticas, y permite que se propongan nuevas funcionalidades. Para esto se deben completar una serie de pasos que se encuentran documentados en el siguiente link: https://github.com/golang/proposal.

Go trata de respetar su filosofía de mantener un lenguaje extremadamente simple y rápido de compilar, por lo que la incorporación de nuevas características que pudieran afectar a uno de estos dos puntos debe poder justificarse claramente, y no debe existir forma alguna de poder llevar a cabo esa tarea con las características actuales del lenguaje. Por ejemplo estas son algunas respuestas que la documentación de Go da sobre la no existencia de excepciones:

“En Go, el manejo de errores es importante. El diseño y las convenciones del idioma lo alientan a verificar explícitamente si ocurren errores (a diferencia de la convención en otros idiomas de arrojar excepciones y, a veces, capturarlas). En algunos casos, esto hace que código de Go sea verboso, pero afortunadamente hay algunas técnicas que puede utilizar para minimizar el manejo de errores repetitivos.” [5]

“Creemos que acoplar excepciones a una estructura de control como en el try-catch-finally, da como resultado un código intrincado. También tiende a alentar a los programadores a etiquetar demasiados errores comunes, como no abrir un archivo, como excepcionales. Go toma un enfoque diferente. Para el manejo simple de errores, los retornos multi-valor de Go facilitan el reporte de un error sin sobrecargar el valor de retorno. Un tipo de error canónico, junto con otras características de Go, hace que el manejo de errores sea agradable, pero bastante diferente del de otros lenguajes.” [3]

Esta filosofía para algunos controvertida es la que creo en mi opinión que hace a Go tan interesante. En vez incorporar constantemente nuevas características y/o copiar otras de otros lenguajes de programación, Go intenta mantener un lenguaje simple, mínimo y conciso.

Proverbios de Go

Los proverbios de Go invitan a los desarrolladores a reflexionar sobre la filosofía de Go y a la enseñanza sobre el lenguaje.

Se invita a los lectores a profundizar más sobre esta filosofía con base en la charla de Rob Pike en el Gopherfest del año 2015: “Go Proverbs”. [44]

A continuación se exponen solo algunos de estos proverbios:

Puede acceder al listado completo y actualizado en https://go-proverbs.github.io.

POO en Go

Imagen - [49]

Este apartado tratará de dar respuesta a la siguiente pregunta: ¿Es Go un lenguaje orientado a objetos?.

Contenido

Objetos

A diferencia de lenguajes explícitamente orientados a objetos como C++, Java, Smalltalk, y Python entre otros, en Go no existen las clases, ni los objetos, ni las excepciones, ni la herencia de clases.

Algunos, rápidamente podrían inferir que Go no es un lenguaje orientado a objetos. ¿Cómo puede existir un lenguaje orientado a objetos que no disponga de clases?. La pregunta que realmente debemos hacernos es: ¿Qué es la programación orientada a objetos?.

Los desarrolladores tenemos una tendencia natural a comparar las cosas. Entonces, por ejemplo, algunos podrían decir que dado que Java es académicamente reconocido como un lenguaje estrictamente orientado a objetos, y dado que ese lenguaje tiene entre otras características clases, objetos y herencia; como Go no las tiene, entonces no podría ser un lenguaje orientado a objetos.

¿Alguien alguna vez escuchó decir que Javascript es un lenguaje orientado a objetos?. Existe una gran discusión sobre si lo es o no lo es - a fin de cuentas en Javascript tampoco hay clases ni herencia de la forma como la que la hay en Java. No obstante Javascript suele ser considerado un lenguaje orientado a objetos. ¿Por qué?. Porque permite implementar ciertas características de la programación orientada a objetos.

Limitar el análisis a si un lenguaje es o no orientado a objetos por la sola existencia de la palabra reservada “class” sería absolutamente simplista e incorrecto. A modo de ejemplo, Objective-C define sus clases sin hacer uso de una palabra “class” y el propio lenguaje se define a sí mismo como proveedor de características orientadas a objetos.

Las clases no caracterizan a los lenguajes orientados a objetos. Un lenguaje soporta el paradigma orientado a objetos si respeta los pilares propios de la programación orientada a objetos. - se verán más adelante -.

Cada lenguaje es único y permite implementar el paradigma orientado a objetos de diversas maneras. Algunas comparaciones de ejemplo:

Como se puede apreciar lenguajes como Scala, Eiffel, C++ y Smalltalk son muy distintos entre sí, pero todos ellos respetan el paradigma orientado a objetos.

Al analizar a Go, debemos comparar qué características propias de la programación orientada a objetos se pueden implementar y no simplemente hacer una comparación de un lenguaje respecto de otro solamente por la falta o no de ciertos atributos o características individuales.

Cómo es la POO en Go

Clases y Objetos

En Go no existen clases ni objetos. Existen tipos de datos definidos por el usuario a los cuales se les pueden incorporar comportamientos. En una analogía con una clase, las propiedades pudieran ser los tipos de datos de una estructura, y los métodos las funciones o comportamientos asociados al tipo de dato.
Ejemplo sobre una estructura:

1 type Persona struct {
2     Nombre   string
3     Apellido string
4     Edad     int
5 }
6 
7 func (p *Persona) Saludar() {
8    fmt.Printf("Nombre: %s, Apellido: %s, Edad: %d\n", p.Nombre, p.Apellido, p.Edad)
9 }

Ejecutar código: https://play.golang.org/p/3uoR7qRs9eV

Ejemplo sobre un tipo de dato especializado:

1 type Int int
2 
3 func (n Int) EsMayorQue(n2 Int) bool {
4    return n > n2;
5 }

Ejecutar código: https://play.golang.org/p/5WXUHGRBfn4

Interfaces

“La interfaz de un objeto no dice nada acerca de su implementación - distintos objetos son libres de implementar las peticiones de forma diferente -. Eso significa que dos objetos con implementaciones completamente diferentes pueden tener interfaces idénticas”. [38]. Go cumple con esta característica de interfaces sin implementación. Lenguajes de programación tales como Visual Basic .Net o Kotlin definen sus interfaces de forma explícita. En Go las interfaces son implícitas ya que no existe ninguna palabra reservada o símbolo para tal fin (tal como implements en Groovy o : en C#). En Go cualquier tipo de dato que implemente todos los métodos de una interfaz, implícitamente la implementa. Este comportamiento es análogo al de algunos lenguajes de tipado dinámico, como Python o Boo, donde a esto se lo conoce como Duck typing (“si camina como un pato y grazna como un pato, entonces debe ser un pato” [54]). En Go el compilador chequea forzosamente que se si referencia a un tipo de dato como una interface, este debe implementar todos sus comportamientos sin excepción.

Ejemplo:

 1 type Felino interface {
 2     Caminar()
 3     Saltar()
 4 }
 5 
 6 type Leon struct {
 7     Nombre string
 8 }
 9 
10 func (t Leon) Caminar() {
11    fmt.Println("CAMINAR")
12 }
13 
14 func (t Leon) Saltar() {
15    fmt.Println("SALTAR")
16 }
17 
18 func SaltarYCaminar(felino Felino) {
19     felino.Saltar()
20     felino.Caminar()
21 }
22 
23 leon := Leon{"Simba"}
24 SaltarYCaminar(leon)

Ejecutar código: https://play.golang.org/p/MD6D893_1KB

Como se puede observar la función SaltarYCaminar() espera una variable de tipo Felino pero se le pasa una de tipo Leon. Como el tipo Leon implementar los métodos Caminar() y Saltar() implícitamente también es un Felino.

Los tres pilares de la programación orientada a objetos

Los tres pilares de la programación orientada a objetos son la herencia, el encapsulamiento y el polimorfismo.

Herencia

Como ya se dijo, en Go no existe la herencia, o al menos no la herencia de clases como se la conoce en otros lenguajes tales como Scala, Swift o Eiffel por ejemplo.

Los tipos de datos en Go permiten ampliar/modificar su comportamiento incrustando otros tipos dentro de él.
Cada estructura incorpora los tipos de datos y comportamientos de la/s estructura/s incrustada/s.

Ejemplo:

 1 type Persona struct {
 2     Nombre   string
 3     Apellido string
 4     Edad     int
 5 }
 6 
 7 type Empleado struct {
 8     Persona
 9     Legajo string
10 }
11 
12 func (p *Persona) DatosBase() {
13    //...
14 }
15 
16 func (e *Empleado) DatosAdicionales() {
17    //...
18 }
19 
20 empleado := Empleado{
21     Persona{"Jose", "Sánchez", 33},
22     "A1234",
23 }
24 empleado.DatosBase()
25 empleado.DatosAdicionales()

Ejecutar código: https://play.golang.org/p/oW83TcMCzHp

También es posible extender/reemplazar el comportamiento de la/s estructura/s incrustrada/s reescribiendo el/los comportamiento/s deseado/s.

Ejemplo:

 1 type Empleado struct {
 2    legajo string
 3    ingreso string
 4 }
 5 
 6 func (e *Empleado) InformarIngreso() {
 7    fmt.Printf("Mi legajo fue el %s.", e.ingreso)
 8 }
 9 
10 func (e *Empleado) Presentarse() {
11    fmt.Printf("Mi legajo es %s.", e.legajo)
12 }
13 
14 type Gerente struct {
15     Empleado
16 }
17 
18 func (g *Gerente) Presentarse() {
19    g.Empleado.Presentarse()
20 
21    fmt.Println(" Mi cargo es el de Gerente.")
22 }
23 
24 gerente := Gerente{
25     Empleado{"A0001", "01-01-2020"},
26 }
27 gerente.Presentarse()
28 gerente.InformarIngreso()

Ejecutar código: https://play.golang.org/p/rBBwVyj9sjQ

En Go entonces se manifiesta la herencia de interfaz. - más información en el siguiente apartado de “Herencia / Composición”.

Encapsulamiento

En Go no existen identificadores de privacidad tales como public, protected y private típicos de otros lenguajes de programación. Go encapsula tipos de datos y funciones a nivel de paquete en base a convenciones de nombres.
Todos aquellos nombres que empiecen con mayúsculas serán accesibles (visibles) desde otros paquetes. Por el contrario, aquellos que comiencen con minúsculas serán privados.
Esta es la razón por la que en el código fuente de paquetes y bibliotecas de Go hay tipos de datos y funciones que pueden comenzar con mayúsculas o minúsculas.

1 // estructura pública
2 type CuentaCorriente struct {
3     //...
4 }
5 
6 // método privado
7 func (cc CuentaCorriente) calcularIntereses() double {
8     //...
9 }

Polimorfismo

Go es polimórfico. Gracias a la herencia de interfaces se pueden asignar referencias en forma polimórfica. - más información en el siguiente apartado de “Herencia / Composición”.

Ejemplo:

 1 type Figura interface {
 2    Dibujar()
 3 }
 4 
 5 type Cuadrado struct {}
 6 
 7 func (c *Cuadrado) Dibujar() {
 8    fmt.Println("Dibujando un Cuadrado.")
 9 }
10 
11 type Triangulo struct {}
12 
13 func (t *Triangulo) Dibujar() {
14     fmt.Println("Dibujando un Triángulo.")
15 }
16 
17 func dibujarFigura(f Figura) {
18     f.Dibujar()
19 }
20 
21 cuadrado := Cuadrado{}
22 dibujarFigura(&cuadrado)
23 
24 triangulo := Triangulo{}
25 dibujarFigura(&triangulo)

Ejecutar código: https://play.golang.org/p/yWlSrH_aXZg

En Go el polimorfismo se logra con la ayuda de interfaces.

Método Estáticos

Esta característica no es posible de replicar en Go dado que cada comportamiento asociado a un tipo de datos, solo puede ser invocado cuando exista un referencia a dicho tipo. Se puede emular esta característica usando el paradigma procedimental del lenguaje. Si bien no es un comportamiento puramente orientado a objetos, se pueden lograr similares resultados.

Ejemplo:

 1 package Modelos
 2 
 3 // estructura
 4 type CuentaCorriente struct {
 5     //...
 6 }
 7 
 8 // emular propiedad estática
 9 // utilizando variable de paquete
10 var banco = "Banco S.A."
11 
12 // emular método estático de CuentaCorriente 
13 // utilizando una función de paquete
14 func CuentaCorrienteGetBanco() string {
15     return banco
16 }

Qué dicen otros autores

Según Gigi Sayfan [6]Go es una extraña mezcla de ideas antiguas y nuevas.”, y “Muchas personas ni siquiera están seguras de si Go es un lenguaje orientado a objetos”, sin embargo para el “Go es un lenguaje de programación orientado a objetos de buena fe. Permite el modelado basado en objetos y promueve las mejores prácticas de usar interfaces en lugar de tipos concretos de jerarquías. Go tiene algunas elecciones sintácticas inusuales, pero el trabajo general con tipos, métodos e interfaces parece simple, ligero y natural. La incrustación no es muy pura, pero aparentemente estaba en juego el pragmatismo, y se proporcionó una incrustación en lugar de solo la composición por nombre”.

Para Junade Ali [39] “La programación orientada a objetos es más que solo clases y objetos; es un paradigma de programación basado alrededor de objetos (estructuras de datos) que contienen campos de datos y métodos. Es esencial entender esto; el uso de clases para organizar un grupo de métodos no relacionados no es orientación a objetos”.

Incluso la propia gente que desarrolla Go responde a esta pregunta [3] como “Si y no”.

Mi punto de vista

Ya que existen otros lenguajes que permiten programar orientado a objetos, sin ser realmente orientados a objetos, puedo decir que Go es un lenguaje no orientado a objetos que permite la programación orientada a objetos - aunque no de la forma tradicional”.

Herencia / Composición

Como se indicó previamente en Go no existe el concepto de herencia de implementación típico de lenguajes orientados a objetos como Java, C++ y Smalltalk entre otros.

En Go existe otro concepto complementario que es la composición.

Tal como menciona Steve Francia [8] “Existen varios enfoques diferentes para definir relaciones entre objetos. Si bien difieren bastante entre sí, todos comparten un propósito común como mecanismo para la reutilización de código”:

Herencia

La herencia se puede expresar de dos maneras: herencia de clases y herencia de interfaces. “La herencia de clases define la implementación de un objeto en términos de la implementación de otro objeto. En resumen, es un mecanismo para compartir código y representación. Por el contrario, la herencia de interfaces (o subtipado) describe cuándo se puede usar un objeto en el lugar de otro.” [38]
No todos los lenguajes de programación implementan la herencia de la misma manera. En algunos lenguajes la herencia de clases y la de interfaces existen como un mismo mecanismo (Eiffel por ejemplo), mientras que en otros están separados (Java por ejemplo). Algunos solamente permiten heredar de un único objeto, esto se denomina herencia simple; mientras otros permiten heredar de varios objetos y a esto se lo denomina herencia múltiple.
Asimismo los comportamientos y datos heredados pueden estar limitados al acceso con el que el objeto padre los definió, esto se denomina visibilidad.

Se expresa a la herencia como una relación es-un/a.

Composición

La composición es una manera de definir objetos dentro de otros objetos. De esta forma un objeto puede adquirir los comportamientos y datos de los otros objetos por los que está compuesto.

Se expresa a la composición como una relación tiene-un/a.

¿Por qué Go no tiene herencia?

Seguramente no haya una respuesta única. No obstante en el faq de la documentación oficial de Go responden a esta pregunta de la siguiente forma [3]:

¿Por qué no hay herencia?:
La programación orientada a objetos, al menos en los lenguajes más conocidos, implica demasiada discusión sobre las relaciones entre tipos, relaciones que a menudo podrían derivarse automáticamente. Go toma un enfoque diferente.
En lugar de requerir que el programador declare de antemano que dos tipos están relacionados, en Go un tipo satisface automáticamente cualquier interfaz que especifique un subconjunto de sus métodos. Además de reducir la administración (la palabra original es bookkeeping), este enfoque tiene ventajas reales. Los tipos pueden satisfacer muchas interfaces a la vez, sin las complejidades de la herencia múltiple tradicional. Las interfaces pueden ser muy livianas - una interfaz con uno o incluso cero métodos puede expresar un concepto útil. Las interfaces se pueden agregar tardíamente si aparece una nueva idea o para probarla - sin anotar los tipos originales. Debido a que no existen relaciones explícitas entre los tipos y las interfaces, no hay una jerarquía de tipos para administrar o discutir.
Es posible utilizar estas ideas para construir algo análogo a los type-safe de las pipes de Unix. Por ejemplo, vea cómo fmt.Fprintf permite la impresión formateada de cualquier salida, no solo de un archivo, o cómo el paquete bufio puede estar completamente separado de la E/S, o cómo el paquete image genera archivos de imágenes comprimidas. Todas estas ideas se derivan de una única interfaz (io.Writer) que representa un único método (Write). Y esto sólo está arañando la superficie. Las interfaces en Go tienen una profunda influencia sobre cómo se estructuran los programas.
Toma un tiempo acostumbrarse, pero este estilo implícito de dependencia de tipos es una de las cosas más productivas sobre Go.”

¿La composición es mejor que la herencia?

En la comunidad de práctica de desarrollo de software existen miles de afirmaciones poco fundadas del estilo ‘esto es mejor que aquello’.
Al incursionar en Go y leer sobre por qué no existe la herencia me encontraba con frases del estilo “la herencia es mala”, “la herencia simple no alcanza pero la herencia múltiple es aún peor”, etc. En más de una oportunidad encontré una referencia a un artículo de JavaWorld [14] donde el autor cuenta la siguiente anécdota:

“Una vez asistí a una reunión del grupo de usuarios de Java, donde James Gosling (el inventor de Java) fue el orador principal. Durante la memorable sesión de preguntas y respuestas, alguien le preguntó: ‘Si pudieras volver a hacer Java, ¿qué cambiarías?’ ‘Suprimiría las clases’, respondió. Después de que la risa se calmó, explicó que el verdadero problema no eran las clases en sí, sino la herencia de implementación (la relación extends). La herencia de interfaz (la relación implements) es preferible. Debe evitar la herencia de implementación siempre que sea posible.”.

Veamos un pequeño ejemplo en Java - ya que permite ambas formas - con algunos pros y contras - basado en el siguiente artículo [15]:

Herencia

 1 class Fruta {
 2     public void pelar() {
 3         System.out.println("Pelando una fruta");
 4     }
 5 }
 6 
 7 class Manzana extends Fruta {
 8     public void pelar() {
 9         System.out.println("Pelando una manzana");
10     }
11 }

Pros:

Contras:

Composición

 1 class Fruta {
 2     public void pelar() {
 3         System.out.println("Pelando una fruta");
 4     }
 5 }
 6 
 7 class Manzana {
 8     private Fruta fruta = new Fruta();
 9 
10     public void pelar() {
11         fruta.pelar();
12     }
13 }

Pros:

Contras:

Ejemplo de Composición en Go

Como veremos la composición en Go se logra embebiendo tipos de datos unos dentro de otros:

 1 type Usuario struct {
 2     nombre   string
 3     apellido string
 4 }
 5 
 6 func (u Usuario) getDatosPersonales() string {
 7     return fmt.Sprintf("%s, %s", u.nombre, u.apellido);
 8 }
 9 
10 type Administrador struct {
11     *Usuario
12     sector string
13 }
14 
15 func (a Administrador) getDatosCompletos() string {
16     return fmt.Sprintf("%s - %s", a.getDatosPersonales(), a.sector);
17 }
18 
19 func main() {
20     var administrador = Administrador{&Usuario{"Jose", "Luis"}, "Computos"};
21 
22     fmt.Println(administrador.getDatosPersonales());
23     fmt.Println(administrador.getDatosCompletos());
24 }

Ejecutar código: https://play.golang.org/p/7W89468lRhC

Composición e interfaces en Go

 1 type Primate interface {
 2     Alimentar(string)
 3 }
 4 
 5 type Antropoide struct {}
 6 
 7 func (t Antropoide) Alimentar(fruta string) {
 8    fmt.Printf("Comiendo %s", fruta)
 9 }
10 
11 type Gorila struct {
12     Antropoide    
13 }
14 
15 func DarDeComer(primate Primate) {
16     primate.Alimentar("banana")
17 }
18 
19 kong := Gorila{}
20 DarDeComer(kong)

Ejecutar código: https://play.golang.org/p/tZhaQwcvez9

Como se puede observar la función DarDeComer() espera una variable de tipo Primate pero se le pasa una de tipo Gorila. Como el tipo Gorila se compone de un Antropoide que implementa el método Alimentar() implícitamente también es un Primate.

¿Cómo afecta esto a la implementación de los Patrones de Diseño GoF?

Como se detallará más adelante, en la implementación de cada patrón de diseño, la falta de herencia será suplida de dos formas diferentes y/o complementarias:

Estrictamente hablando de programación orientada a objetos, la mayor dificultad encontrada es cuando una clase abstracta implementa un método concreto con comportamiento que llama a métodos abstractos también definidos en dicha clase abstracta que luego serán implementados en las clases hijas.
Para emular este comportamiento la estrategia utilizada en esta publicación será pasar como un argumento del método concreto de la clase abstracta una referencia de una interface que exponga cuáles serán los métodos abstractos que serán implementados por las clases hijas que implementen esa interface.

Veamos un ejemplo para entender la estrategia:

Problema a resolver - código Java

 1 abstract class ClaseAbstracta {
 2     public void metodoConcreto() {
 3         this.metodoAbstracto();
 4     }
 5 
 6     abstract public void metodoAbstracto();
 7 }
 8 
 9 class ClaseHija extends ClaseAbstracta {
10     @Override
11     public void metodoAbstracto() {
12         System.out.println("Soy metodo abstracto");
13     }
14 }

Estrategia implementada en Go

 1 type InterfaceMetodosAbstractos interface {
 2     MetodoAbstracto()
 3 }
 4 
 5 type ClaseAbstracta struct{}
 6 
 7 func (ca *ClaseAbstracta) MetodoConcreto(self *InterfaceMetodosAbstractos) {
 8     self.MetodoAbstracto()
 9 }
10 
11 type ClaseHija struct {
12     *ClaseAbstracta
13 }
14 
15 func (ch *ClaseHija) MetodoAbstracto() {
16     fmt.Println("Soy metodo abstracto")
17 }
18 
19 claseHija := &ClaseHija{&ClaseAbstracta{}}
20 claseHija.MetodoConcreto(&claseHija)

Ejecutar código: https://play.golang.org/p/NnEeU5Z4XWI

Conclusión

Go permite la composición y la herencia de interfaz. El uso de interfaces (es-un) y de la composición (tiene-un) posibilitan la reutilización de código en Go y la adopción de técnicas y de patrones orientados a objetos.

S.O.L.I.D

SOLID es un acrónimo introducido por Robert C. Martin en su libro “Agile Software Development, Principles, Patterns and Practices” [40] y hace referencia a los siguientes cinco principios:

El objetivo de aplicar estos principios es obtener sistemas orientados a objetos con código de mayor calidad, facilidad de mantenimiento y mejores oportunidades de reuso de código.

Principio de responsabilidad única

La primera observación respecto de este principio es que en Go no existen clases. Sin embargo, como vimos mediante la incorporación de comportamientos a tipos de datos, podemos llegar a un concepto equivalente.

Este principio hace foco en que un objeto debe tener únicamente una responsabilidad encapsulada por la clase. Cuando se hace referencia a una responsabilidad es para referirse a una razón para cambiar.
Mantener una clase que tiene múltiples objetivos o responsabilidades es mucho más complejo que una clase enfocada en una única responsabilidad.

El siguiente ejemplo no cumple con este principio porque otorga a una estructura dos responsabilidades bien diferenciadas: guardar en un archivo local y guardar en una base de datos.

 1 package main
 2 
 3 type Documento struct {
 4     Nombre  string
 5 }
 6 
 7 func (d *Documento) GuardarEnArchivo() {
 8    // ...
 9 }
10 
11 func (d *Documento) GuardarEnBaseDatos() {
12    // ...
13 }

Ejecutar código: https://play.golang.org/p/1d5JvLzQTEH

Un Documento debería saber cómo acceder al sistema de archivos local y a la vez como conectarse y operar contra una base de datos. Implementar ambas acciones en una misma estructura genera un alto acoplamiento.

Creando estructuras con responsabilidades bien definidas se puede mejorar el código de la siguiente manera:

1 package model
2 
3 type Documento struct {
4     Nombre  string
5 }
 1 package store
 2 
 3 import "model"
 4 
 5 type GuardadorDocumento interface {
 6     Guardar(d model.Documento)
 7 }
 8 
 9 type GuardadorDocumentoArchivo struct {
10     Path  string
11 }
12 
13 func (gda GuardadorDocumentoArchivo) Guardar(d model.Documento) {
14    // ...
15 }
16 
17 type GuardadorDocumentoBaseDatos struct {
18     StringConnection  string
19 }
20 
21 func (gdbd GuardadorDocumentoBaseDatos) Guardar(d model.Documento) {
22    // ...
23 }

Ejecutar código: https://play.golang.org/p/nxRPMtLbWP2

¿Qué pasa en Go: Gracias a la organización en paquetes que permite Go es posible crear estructuras, tipos, funciones y métodos empaquetados con propósitos claros y bien definidos.

Principio abierto cerrado

Este principio propone que una entidad esté cerrada, lista para ser usada y estable en su calidad e interfaces, y al mismo tiempo abierta, es decir, que permita extender su comportamiento (pero sin modificar su código fuente).

Imaginemos que se requiere cambiar el comportamiento de la siguiente estructura únicamente en uno de sus métodos:

 1 type Animal struct {
 2     Nombre  string
 3 }
 4 
 5 func (a Animal) Caminar() {
 6     // ...
 7 }
 8 
 9 func (a Animal) Saltar() {
10     // ...
11 }

Ejecutar código: https://play.golang.org/p/FP80Iem9qqV

Podemos hacerlo de la siguiente forma:

1 type AnimalModificado struct {
2     Animal
3 }
4 
5 func (am AnimalModificado) Caminar() {
6     // ...
7 }

Ejecutar código: https://play.golang.org/p/Sf1WkRugRN3

¿Qué pasa en Go: Gracias a la composición que permite Go es posible componer tipos simples en más complejos manteniendo la interfaz del tipo original.

Principio de substitución de Liskov

Este principio propone que el contrato de una clase base debe ser honrado por sus clases derivadas para que instancias de las clases derivadas puedan reemplazar a instancias de la clase base.

Veamos el siguiente código:

 1 type RespuestaJSON struct {}
 2 
 3 func (r RespuestaJSON) Imprimir() {
 4     // ...
 5 }
 6 
 7 type RespuestaHTML struct {}
 8 
 9 func (r RespuestaHTML) Imprimir() {
10     // ...
11 }
12 
13 type Emision struct {}
14 
15 func (e Emision) EmitirJSON(r RespuestaJSON) {
16     // ...
17 }
18 
19 func (e Emision) EmitirHTML(r RespuestaHTML) {
20     // ...
21 }

Ejecutar código: https://play.golang.org/p/4jwdJ0NUjOe

La estructura Emision debe implementar dos comportamientos, ya que debe poder gestionar impresiones en HTML y JSON. Si a futuro se requiriera de otro tipo de impresión - xml por ejemplo - se debería modificar su código fuente.

La siguiente modificación permite intercambiar cualquier tipo de respuesta para su impresión:

 1 type Respuesta interface {
 2     Imprimir()
 3 }
 4 
 5 type RespuestaJSON struct {}
 6 
 7 func (r RespuestaJSON) Imprimir() {
 8     // ...
 9 }
10 
11 type RespuestaHTML struct {}
12 
13 func (r RespuestaHTML) Imprimir() {
14     // ...
15 }
16 
17 type Emision struct {}
18 
19 func (e Emision) Emitir(r Respuesta) {
20     // ...
21 }

Ejecutar código: https://play.golang.org/p/ZJ0iEXpWgt4

¿Qué pasa en Go: Al definir firmas de métodos a través de interfaces, y no mediante tipos concretos, es posible utilizar cualquier tipo que respete implícitamente la interfaz.

Principio de segregación de la interfaz

Este principio hace foco en como deben definirse las interfaces. Las mismas deben ser pequeñas y específicas.
Grandes y complejas interfaces obligan al cliente a implementar métodos que no necesita.

Veamos la siguiente interface:

 1 type Boton interface {
 2     OnClick()
 3     OnDobleClick()
 4 }
 5 
 6 type BotonLogin struct {}
 7 
 8 func (b BotonLogin) OnClick() {
 9     // ...
10 }
11 
12 func (b BotonLogin) OnDobleClick() {
13     // vacio
14 }

Ejecutar código: https://play.golang.org/p/FHu4-lVUJ2D

Como puede verse la interface Boton obliga a implementar ambos comportamientos en sus clientes cuando es muy factible que no todos ellos deban implementar ambas opciones.

Una solución podría ser la siguiente:

 1 type OnClickListener interface {
 2     OnClick()
 3 }
 4 
 5 type OnDobleClickListener interface {
 6     OnDobleClick()
 7 }
 8 
 9 type BotonLogin struct {}
10 
11 func (b BotonLogin) OnClick() {
12     // ...
13 }
14 
15 type BotonIcono struct {}
16 
17 func (b BotonIcono) OnDobleClick() {
18     // ...
19 }

Ejecutar código: https://play.golang.org/p/umwCkd0*eKQ

¿Qué pasa en Go: En Go puede aplicarse este concepto aislando el comportamiento requerido utilizando interfaces más pequeñas.

Principio de inversión de la dependencia

Este principio esta basado en reducir las dependencias entre los módulos del código para atacar el alto acoplamiento.

Veamos la siguiente ejemplo:

1 type B struct {}
2 
3 func (b B) Saludar() {
4     // ...
5 }
6 
7 type A struct {
8     B
9 }

Ejecutar código: https://play.golang.org/p/ANqhCiBv3Zr

Como puede verse el tipo A depende del tipo B por lo que si el tipo B es modificado afectará al tipo A.

Una solución podría ser la siguiente:

 1 type Saludador interface {
 2     Saludar()
 3 }
 4 
 5 type B struct {}
 6 
 7 func (b B) Saludar() {
 8     // ...
 9 }
10 
11 type A struct {
12     Saludador
13 }

Ejecutar código: https://play.golang.org/p/pHDUY3VCNTI

¿Qué pasa en Go: Componer tipos mediante interfaces, y no mediante tipos concretos, permite evitar una fuerte dependencia entre tipos.

Conclusión

Si bien el libro de Robert C. Martin [40] tiene más de una década y media y hace referencia a lenguajes propiamente orientados a objetos, vimos como también pueden aplicarse esos principios en Go.

Como se vio en el apartado anterior, el poder de la composición y de las interfaces implícitas le permiten a Go implementar buenas prácticas y conceptos propios de la programación orientada a objetos.

Parte II

Patrones de Diseño GoF en Go

Patrones de Diseño

Imagen - [49]

Este apartado tratará sobre ¿Qué es un Patrón de Diseño? y ¿Qué es GoF?.

Patrones de Diseño

En el libro de Erich Gamma et al - “Patrones de Diseño” [38] - exponen la importancia y usos de los patrones de diseño en conceptos como:

Como se puede observar se utilizan términos como clases y objetos, ya que los patrones de diseño están enfocados al desarrollo orientado a objetos siendo los ejemplos de su libro implementados en C++.

En las siguientes secciones se detallará como es posible implementar dichos patrones de diseño en Go.

Contenido

GoF

¿Qué es Gof?

En esta publicación se utilizarán los 23 patrones de diseño del libro “Patrones de Diseño: Elementos de software orientado a objetos reutilizable” [38] escrito por Erich Gamma, Richard Helm, Ralph Johnson y John Vlissides.
El término GoF proviene de los autores del libro que son conocidos por la comunidad como Gang of Four (La Banda de los Cuatro).

¿Es posible implementar los patrones de diseño en Go?

En el libro “Patrones de Diseño” [38] los autores hacen la siguiente aclaración respecto de los lenguajes utilizados para documentar los 23 patrones: “Aunque los patrones describen diseños orientados a objetos, están basados en soluciones prácticas que han sido implementadas en los lenguajes de programación orientados a objetos más usuales, como Smalltalk y C++, en vez de mediante lenguajes procedimentales (Pascal, C, Ada) u otros lenguajes orientados a objetos más dinámicos (CLOS, Dylan, Self). Nosotros hemos elegido Smalltalk y C++ por una cuestión pragmática: nuestra experiencia diaria ha sido con estos lenguajes, y estos cada vez son más populares”. “La elección del lenguaje de programación es importante, ya que influye en el punto de vista. Nuestros patrones presuponen características de Smalltalk y C++, y esa elección determina lo que puede implementarse o no fácilmente. Si hubiéramos supuesto lenguajes procedimentales, tal vez, hubiéramos incluido patrones llamados ‘Herencia’, ‘Encapsulación’ y ‘Polimorfismo’. De manera similar, algunos de nuestros patrones están incluidos directamente en lenguajes orientados a objetos menos corrientes. CLOS, por ejemplo, tiene multi-métodos que reducen la necesidad de patrones como Visitor. De hecho, hay suficientes diferencias entre Smalltalk y C++ como para que algunos patrones puedan expresarse más fácilmente en un lenguaje que en otro (por ejemplo, el Iterator)”

También es importante entender que este trabajo intenta demostrar cómo pueden aplicarse los patrones de diseño en Go, aunque no es necesariamente imperativo su uso ni se lo promueve. Tal como exponen los autores, cada lenguaje tiene sus particularidades y en necesario entender el real problema a resolver, y no intentar adaptar formas de trabajo de un lenguaje a otro, sino entender esas particularidades que hacen diferente a un lenguaje respecto del otro para potenciar sus características.

A pesar de que parezca desalentador lo anteriormente dicho es importante remarcarlo. Un error muy común cuando aprendemos algo nuevo es querer utilizarlo en cualquier contexto, sin analizar realmente la conveniencia de su uso. Cuando uno aprende un patrón de diseño nuevo se ve tentado a utilizarlo. Los patrones de diseño resuelven problemas conocidos, y solo deben usarse si se está en presencia de un problema apropiado.

Un error que veo muy seguido es la adaptación de un patrón de un lenguaje a otro (como si se tratase de una traducción literal) cuando en realidad no se tienen en cuenta sus características específicas, que hacen a cada lenguaje de programación único, ni el objetivo que intenta resolver el patrón de diseño. Intentaré no caer yo mismo en este error.

¿Cómo se clasifican los patrones?

Los patrones de diseño se organizan en tres familias de acuerdo a su propósito:

Los autores del libro “Patrones de Diseño” [38], también proponen otro criterio de clasificación denominado ámbito: “especifica si el patrón se aplica principalmente a clases o a objetos. Los patrones de clases se ocupan de relaciones entre las clases y sus subclases. Esras relaciones se establecen a través de la herencia, de modo que son relaciones estáticas - fijadas en tiempo de compilación -. Los patrones de objetos tratan con las relaciones entre objetos, que pueden cambiarse en tiempo de ejecución y son más dinámicas”.

Catálogo de patrones

Los patrones de diseño se catalogan de la siguiente forma:

Según su Propósito:

Según su Ámbito:

Patrones de Clase

Creación Estructurales Comportamiento
Factory Method Adapter Interpreter
    Template Method

Patrones de Objeto

Creación Estructurales Comportamiento
Abstract Factory Adapter Chain of Responsibility
Builder Bridge Command
Propotype Composite Iterator
Singleton Decorator Mediator
  Facade Memento
  Flyweight Observer
  Proxy State
    Strategy
    Visitor

¿Cómo se documenta un patrón?

Los autores del libro “Patrones de Diseño” [38] utilizan la siguiente plantilla para especificar un Patrón.

Sección Detalle
Nombre del patrón y clasificación El nombre del patrón transmite su esencia. El nombre es vital porque pasa a formar parte de nuestro vocabulario de diseño. Por ejemplo al decir Singleton todos entenderán de que se habla, sin necesidad de explicar el objetivo y los participantes del patrón.
Propósito Es una frase breve que responde a las siguientes cuestiones: ¿Qué hace este patrón de diseño?, ¿En qué se basa? ¿Cuál es el problema concreto de diseño que resuelve?.
También conocido como Son otros nombres, si existen, por los que también se conoce al patrón.
Motivación Un escenario que ilustra un problema de diseño y cómo las estructuras de clases y objetos del patrón resuelven el problema.
Aplicabilidad ¿En qué situaciones se puede aplicar el patrón de diseño? ¿Qué ejemplos hay de malos diseños que el patrón puede resolver? ¿Cómo se puede reconocer dichas situaciones?.
Estructura Una representación gráfica de las clases del patrón.
Participantes Las clases y objetos participantes en el patrón de diseño, junto con sus responsabilidades.
Colaboradores Cómo colaboran los participantes para llevar a cabo sus responsabilidades.
Consecuencias ¿Cómo consigue el patrón sus objetivos? ¿Cuáles son las ventajas, desventajas y resultados de usar el patrón?.
Implementación ¿Cuáles son las dificultades, trucos o técnicas que deberíamos tener en cuenta a la hora de aplicar el patrón? ¿Hay cuestiones específicas del lenguaje?.
Código de ejemplo Fragmentos de código que muestran cómo se puede implementar el patrón.
Usos conocidos Ejemplos del patrón en sistemas reales.
Patrones relacionados ¿Qué patrones de diseño están estrechamente relacionados con este? ¿Cuáles son las principales diferencias? ¿Con qué otros patrones debería usarse?.

En esta publicación utilizaremos la misma estructura pero con las siguientes observaciones:

Implicancias

En las explicaciones de cada patrón de diseño del libro “Patrones de Diseño” [38] utilizan constantemente las palabras objeto y clase. Parte de la comunidad asume que en Go la palabra objeto es válida ya que se interpreta que es sinónimo de un tipo de dato con comportamiento. Sin embargo, a fines de esta publicación cuando se requiera hablar de un objeto utilizaré la frase “variable” (considero que se ajusta más al lenguaje).
Para la palabra clase no existe una terminología comparable, por lo que utilizaré simplemente la frase “tipo de dato” o la palabra “tipo”.
En cuanto a la palabra método, si bien es válida en Go dado que en definitiva los métodos son funciones que reciben un argumento receptor, también utilizaré la frase “comportamiento de un tipo”.

Dado que el libro original “Patrones de Diseño” [38] utiliza OMT en lugar de UML, se hará el mayor esfuerzo en respetar la estructura original de cada patrón dado que cada lenguaje permite implementaciones levemente distintas basándose en sus características particulares.
Dicho esto se han observado pequeñas alteraciones/concesiones en implementaciones donde se reemplazan clases abstractas por interfaces y viceversa. Este tema es justamente una de las principales diferencias que pueden encontrarse en las implementaciones de un patrón de diseño GoF en Go, por lo que será dificultoso al lector diferenciar si una clase abstracta fue implementada como interface por a) una necesidad exclusiva dada las limitaciones del lenguaje Go en sus características orientadas a objetos; o b) simplemente por una concesión válida entre los desarrolladores de otros lenguajes. Lo invito en esos caso a buscar implementaciones de código en otros lenguajes como Java o C#.

Patrones de Comportamiento

Imagen - [49]

Según el libro “Patrones de Diseño” [38] los patrones de comportamiento “tienen que ver con algoritmos y con la asignación de responsabilidades a objetos. Los patrones de comportamiento describen no solo patrones de clases y objetos, sino también patrones de comunicación entre ellos.”

Contenido

Strategy

Propósito

Según el libro “Patrones de Diseño” [38] el patrón Strategy “define una familia de algoritmos, encapsula cada uno de ellos y los hace intercambiables. Permite que un algoritmo varíe independientemente de los clientes que lo usan”.

También conocido como

Policy

Estructura

Participantes

Implementación

No se observan impedimentos y/o modificaciones de la estructura original del patrón para su implementación en Go.

Código de ejemplo

En este ejemplo queremos definir tres estrategias concretas que pueden realizar distintas operaciones matemáticas. Cuando se crea el contexto se establece que estrategia deberá utilizar.

Implementación:

 1 // Interface
 2 type Estrategia interface {
 3     RealizarOperacion(int, int) int
 4 }
 5 
 6 // Estrategia Suma
 7 type EstrategiaSuma struct{}
 8 
 9 func (e EstrategiaSuma) RealizarOperacion(num1 int, num2 int) int {
10     return num1 + num2
11 }
12 
13 // Estrategia Resta
14 type EstrategiaResta struct{}
15 
16 func (e EstrategiaResta) RealizarOperacion(num1 int, num2 int) int {
17     return num1 - num2
18 }
19 
20 // Estrategia Multiplica
21 type EstrategiaMultiplica struct{}
22 
23 func (e EstrategiaMultiplica) RealizarOperacion(num1 int, num2 int) int {
24     return num1 * num2
25 }
26 
27 // Contexto
28 type Contexto struct {
29     estrategia Estrategia
30 }
31 
32 func (c *Contexto) EjecutarOperacion(num1 int, num2 int) int {
33     return c.estrategia.RealizarOperacion(num1, num2)
34 }

Se puede probar la implementación del patrón de la siguiente forma:

 1 var contexto Contexto
 2 num1 := 10
 3 num2 := 5
 4 
 5 contexto = Contexto{EstrategiaSuma{}}
 6 fmt.Printf("%d + %d = %d\n", num1, num2, contexto.EjecutarOperacion(num1, num2))
 7 
 8 contexto = Contexto{EstrategiaResta{}}
 9 fmt.Printf("%d - %d = %d\n", num1, num2, contexto.EjecutarOperacion(num1, num2))
10 
11 contexto = Contexto{EstrategiaMultiplica{}}
12 fmt.Printf("%d * %d = %d\n", num1, num2, contexto.EjecutarOperacion(num1, num2))

Ejecutar código: https://play.golang.org/p/OoMEcPgef7e

Código de ejemplo: https://github.com/danielspk/designpatternsingo/tree/master/patrones/comportamiento/strategy

Chain of Responsibility

Propósito

Según el libro “Patrones de Diseño” [38] el patrón Chain of Responsibility “evita acoplar el emisor de una petición a su receptor, dando a más de un objeto la posibilidad de responder a la petición. Encadena los objetos receptores y pasa la petición a través de la cadena hasta que es procesada por algún objeto”.

Estructura

Participantes

Implementación

Código de ejemplo

En este ejemplo se definen dos receptores distintos de mensajes. Uno para mensajes de alta prioridad y otro para mensajes de baja prioridad. El mensaje enviado por el cliente es transmitido a través de la cadena de receptores y cada receptor trata o no el mensaje de acuerdo a su prioridad.

Implementación:

 1 // Interface
 2 type Receptor interface {
 3     ProcesarMensaje(int, string) string
 4 }
 5 
 6 // Receptor de Alta Prioridad
 7 type ReceptorAltaPrioridad struct{
 8     siguiente Receptor
 9 }
10 
11 func (rap ReceptorAltaPrioridad) ProcesarMensaje(prioridad int, mensaje string) string {
12     if prioridad >= 5 {
13        return "Procesando mensaje de alta prioridad: " + mensaje
14     }
15 
16     if rap.siguiente != nil {
17        return rap.siguiente.ProcesarMensaje(prioridad, mensaje)
18     }
19 
20     return ""
21 }
22 
23 // Receptor de Baja Prioridad
24 type ReceptorBajaPrioridad struct{
25     siguiente Receptor
26 }
27 
28 func (rbp ReceptorBajaPrioridad) ProcesarMensaje(prioridad int, mensaje string) string {
29     if prioridad < 5 {
30        return "Procesando mensaje de baja prioridad: " + mensaje
31     }
32 
33     if rbp.siguiente != nil {
34        return rbp.siguiente.ProcesarMensaje(prioridad, mensaje)
35     }
36 
37     return ""
38 }

Se puede probar la implementación del patrón de la siguiente forma:

1 manejadores := ReceptorBajaPrioridad {
2     siguiente: ReceptorAltaPrioridad {},
3 }
4 
5 fmt.Println(manejadores.ProcesarMensaje(4, "Mensaje 1 - Prioridad 4"))
6 fmt.Println(manejadores.ProcesarMensaje(5, "Mensaje 2 - Prioridad 5"))
7 fmt.Println(manejadores.ProcesarMensaje(10, "Mensaje 3 - Prioridad 10"))

Ejecutar código: https://play.golang.org/p/TnwdRltyBds

Código de ejemplo: https://github.com/danielspk/designpatternsingo/tree/master/patrones/comportamiento/chainofresponsibility

Command

Propósito

Según el libro “Patrones de Diseño” [38] el patrón Command “encapsula una petición en un objeto, permitiendo así parametrizar a los clientes con diferentes peticiones, hacer cola o llevar registro de las peticiones, y poder deshacer las operaciones”.

También conocido como

Action, Transaction

Estructura

Participantes

Implementación

No se observan impedimentos y/o modificaciones de la estructura original del patrón para su implementación en Go.

Código de ejemplo

En este ejemplo queremos poder prender y apagar un televisor mediante la invocación de comandos mediante un control remoto.

Implementación:

 1 // Interface Comando (Orden)
 2 type Comando interface {
 3     Ejecutar() string
 4 }
 5 
 6 // Comando Prender (OrdenConcreta)
 7 type ComandoPrender struct {
 8     receptor Receptor
 9 }
10 
11 func (cp ComandoPrender) Ejecutar() string {
12     return cp.receptor.Prender()
13 }
14 
15 // Comando Apagar (OrdenConcreta)
16 type ComandoApagar struct {
17     receptor Receptor
18 }
19 
20 func (ca ComandoApagar) Ejecutar() string {
21     return ca.receptor.Apagar()
22 }
23 
24 // Invocador
25 type Invocador struct {
26     comandos []Comando
27 }
28 
29 func (i *Invocador) GuardarComando(comando Comando) {
30     i.comandos = append(i.comandos, comando)
31 }
32 
33 func (i *Invocador) EliminarUltimoComando() {
34     if len(i.comandos) != 0 {
35         i.comandos = i.comandos[:len(i.comandos)-1]
36     }
37 }
38 
39 func (i *Invocador) Limpiar() {
40     i.comandos = []Comando{}
41 }
42 
43 func (i *Invocador) Ejecutar() string {
44     var resultados string
45 
46     for _, comando := range i.comandos {
47         resultados += comando.Ejecutar() + "\n"
48     }
49 
50     return resultados
51 }
52 
53 // Receptor
54 type Receptor struct{}
55 
56 func (r Receptor) Prender() string {
57     return "- Prender Televisor"
58 }
59 
60 func (r Receptor) Apagar() string {
61     return "- Apagar Televisor"
62 }

Se puede probar la implementación del patrón de la siguiente forma:

 1 invocador := Invocador{comandos: []Comando{}}
 2 receptor := Receptor{}
 3 
 4 // se establecen dos comandos concretos y se los elimina
 5 // el invocador queda sin comandos que ejecutar
 6 invocador.GuardarComando(ComandoPrender{receptor: receptor})
 7 invocador.GuardarComando(ComandoApagar{receptor: receptor})
 8 invocador.Limpiar()
 9 
10 // se establecen dos comandos concretos iguales y se elimina el último
11 // el invocador queda con un único comando para ejecutar
12 invocador.GuardarComando(ComandoPrender{receptor: receptor})
13 invocador.GuardarComando(ComandoPrender{receptor: receptor})
14 invocador.EliminarUltimoComando()
15 
16 // se establece un comando concreto más
17 invocador.GuardarComando(ComandoApagar{receptor: receptor})
18 
19 // el invocador ejecuta los dos comandos
20 fmt.Printf("Comandos ejecutados:\n%v\n", invocador.Ejecutar())

Ejecutar código: https://play.golang.org/p/BRtWoVLF5nB

Código de ejemplo: https://github.com/danielspk/designpatternsingo/tree/master/patrones/comportamiento/command

Template Method

Propósito

Según el libro “Patrones de Diseño” [38] el patrón Template Method “define en una operación el esqueleto de un algoritmo, delegando en las subclases algunos de sus pasos. Permite que las subclases redefinan ciertos pasos de un algoritmo sin cambiar su estructura”.

Estructura

Participantes

Implementación

Código de ejemplo

En este ejemplo queremos cumplir con una serie de pasos formales (método plantilla) para desplegar diferentes aplicaciones móviles.

Implementación:

 1 // Clase Abstracta - Interface
 2 type DeployInterface interface {
 3     Testear()
 4     Compilar()
 5     Publicar()
 6 }
 7 
 8 // Clase Abstracta
 9 type Deploy struct{}
10 
11 // Método Plantilla
12 func (d Deploy) Construir(di DeployInterface) {
13     fmt.Println("Ejecutando las siguientes acciones:")
14 
15     di.Testear()
16     di.Compilar()
17     di.Publicar()
18 }
19 
20 // Clase Concreta - Android
21 type DeployAndroid struct {
22     Deploy
23 }
24 
25 func (d DeployAndroid) Testear() {
26     fmt.Println("Android: Testeando")
27 }
28 
29 func (d DeployAndroid) Compilar() {
30     fmt.Println("Android: Compilando")
31 }
32 
33 func (d DeployAndroid) Publicar() {
34     fmt.Println("Android: Publicando")
35 }
36 
37 // Clase Concreta - iOS
38 type DeployiOS struct {
39     Deploy
40 }
41 
42 func (d DeployiOS) Testear() {
43     fmt.Println("iOS: Testeando")
44 }
45 
46 func (d DeployiOS) Compilar() {
47     fmt.Println("iOS: Compilando")
48 }
49 
50 func (d DeployiOS) Publicar() {
51     fmt.Println("iOS: Publicando")
52 }

Se puede probar la implementación del patrón de la siguiente forma:

1 deployAndroid := DeployAndroid{Deploy{}}
2 deployAndroid.Construir(&deployAndroid)
3 
4 deployiOS := DeployiOS{Deploy{}}
5 deployiOS.Construir(&deployiOS)

Ejecutar código: https://play.golang.org/p/1J-MIDMaXi5

Código de ejemplo: https://github.com/danielspk/designpatternsingo/tree/master/patrones/comportamiento/templatemethod

Implementación alternativa:

En esta alternativa no es necesario pasar la propia referencia del tipo concreto en el método Construir. La construcción del tipo concreto se realiza componiéndolo con un tipo abstracto compuesto con el mismo tipo concreto.

 1 // Clase Abstracta - Interface
 2 type DeployInterface interface {
 3     Testear()
 4     Compilar()
 5     Publicar()
 6 }
 7 
 8 // Clase Abstracta
 9 type Deploy struct{
10     DeployInterface
11 }
12 
13 // Método Plantilla
14 func (d Deploy) Construir() {
15     fmt.Println("Ejecutando las siguientes acciones:")
16 
17     d.Testear()
18     d.Compilar()
19     d.Publicar()
20 }
21 
22 // Clase Concreta - Android
23 type DeployAndroid struct {
24     Deploy
25 }
26 
27 func (d DeployAndroid) Testear() {
28     fmt.Println("Android: Testeando")
29 }
30 
31 func (d DeployAndroid) Compilar() {
32     fmt.Println("Android: Compilando")
33 }
34 
35 func (d DeployAndroid) Publicar() {
36     fmt.Println("Android: Publicando")
37 }

Se puede probar la implementación alternativa del patrón de la siguiente forma:

1 deployAndroid := DeployAndroid{Deploy{DeployAndroid{}}}
2 deployAndroid.Construir()

Ejecutar código: https://play.golang.org/p/u5df18NIRI1

Memento

Propósito

Según el libro “Patrones de Diseño” [38] el patrón Memento “representa y externaliza el estado interno de un objeto sin violar la encapsulación, de forma que este puede volver a dicho estado más tarde”.

También conocido como

Token

Estructura

Participantes

Implementación

No se observan impedimentos y/o modificaciones de la estructura original del patrón para su implementación en Go.

Código de ejemplo

En este ejemplo queremos que un editor de texto tenga la posibilidad de volver atrás su estado luego de una actualización de contenido.

Implementación:

 1 // Interface
 2 type Memento interface {
 3     SetContenido(string)
 4     GetContenido() string
 5 }
 6 
 7 // Memento
 8 type EditorMemento struct {
 9      contenido string
10 }
11 
12 func (em *EditorMemento) SetContenido(contenido string) {
13     em.contenido = contenido
14 }
15 
16 func (em *EditorMemento) GetContenido() string {
17     return em.contenido
18 }
19 
20 // Originador
21 type Editor struct {
22     contenido string
23 }
24 
25 func (e *Editor) VerContenido() string {
26     return e.contenido
27 }
28 
29 func (e *Editor) Escribir(texto string) {
30     e.contenido = e.contenido + " " + texto
31 }
32 
33 func (e *Editor) Guardar() Memento {
34     editorMemento := &EditorMemento{}
35     editorMemento.SetContenido(e.contenido)
36 
37     return editorMemento
38 }
39 
40 func (e *Editor) Restaurar(memento Memento) {
41     e.contenido = memento.GetContenido()
42 }

Se puede probar la implementación del patrón de la siguiente forma:

 1 editor := &Editor{}
 2 editor.Escribir("TextoA")
 3 editor.Escribir("TextoB")
 4 fmt.Printf("El editor contiene:%s\n", editor.VerContenido())
 5 
 6 fmt.Println("Se guarda el estado actual")
 7 memento := editor.Guardar()
 8 
 9 fmt.Println("Se escribe unnuevo contenido")
10 editor.Escribir("TextoC")
11 
12 fmt.Printf("El editor ahora contiene:%s\n", editor.VerContenido())
13 
14 fmt.Println("Se restaura el contenido guardado")
15 editor.Restaurar(memento)
16 
17 fmt.Printf("El editor nuevamente contiene:%s\n", editor.VerContenido())

Ejecutar código: https://play.golang.org/p/4o78qJhd-h2

Código de ejemplo: https://github.com/danielspk/designpatternsingo/tree/master/patrones/comportamiento/memento

Interpreter

Propósito

Según el libro “Patrones de Diseño” [38] el patrón Interpreter “dado un lenguaje, define una representación de su gramática junto con un intérprete que usa dicha representación para interpretar sentencias del lenguaje”.

Estructura

Participantes

Implementación

Código de ejemplo

En este ejemplo queremos interpretar distintas expresiones lógicas: AND y OR en base a palabras definidas en un contexto.

Implementación:

 1 // Contexto
 2 type Contexto struct {
 3     Palabra string
 4 }
 5 
 6 // Interface
 7 type ExpresionAbstracta interface {
 8     Interpretar(Contexto) bool
 9 }
10 
11 // Expresion Terminal
12 type ExpresionTerminal struct {
13     Palabra string
14 }
15 
16 func (et *ExpresionTerminal) Interpretar(contexto Contexto) bool {
17     return contexto.Palabra == et.Palabra
18 }
19 
20 // Expresion No Terminal
21 type ExpresionOR struct {
22     expresionA ExpresionAbstracta
23     expresionB ExpresionAbstracta
24 }
25 
26 func (eo *ExpresionOR) Interpretar(contexto Contexto) bool {
27     return eo.expresionA.Interpretar(contexto) || eo.expresionB.Interpretar(contexto)
28 }
29 
30 // Expresion No Terminal
31 type ExpresionAND struct {
32     expresionA ExpresionAbstracta
33     expresionB ExpresionAbstracta
34 }
35 
36 func (ea *ExpresionAND) Interpretar(contexto Contexto) bool {
37     return ea.expresionA.Interpretar(contexto) && ea.expresionB.Interpretar(contexto)
38 }

Se puede probar la implementación del patrón de la siguiente forma:

 1 expresionA := &ExpresionTerminal{"Perro"}
 2 expresionB := &ExpresionTerminal{"Gato"}
 3 expresionC := &ExpresionTerminal{"Perro"}
 4 
 5 contextoOR := Contexto{"Perro"}
 6 expresionOR := &ExpresionOR{expresionA, expresionB}
 7 fmt.Printf("La expresion OR contiene la palabra perro: %v\n", expresionOR.Interpretar(conte\
 8 xtoOR))
 9 
10 contextoAND := Contexto{"Perro"}
11 expresionAND := &ExpresionAND{expresionA, expresionC}
12 fmt.Printf("La expresion AND contiene dos palabras perro: %v\n", expresionAND.Interpretar(c\
13 ontextoAND))

Ejecutar código: https://play.golang.org/p/zmXhDClx5k7

Código de ejemplo: https://github.com/danielspk/designpatternsingo/tree/master/patrones/comportamiento/interpreter

Iterator

Propósito

Según el libro “Patrones de Diseño” [38] el patrón Iterator “proporciona un modo de acceder secuencialmente a los elementos de un objeto agregado sin exponer su representación interna”.

También conocido como

Cursor

Estructura

Participantes

Implementación

Código de ejemplo

En este ejemplo queremos recorrer las distintas estaciones de radio preseteadas de un estéreo de audio.

Implementación:

 1 // Iterador Interface
 2 type Iterador interface {
 3     Valor() string
 4     Siguiente()
 5     Anterior()
 6 }
 7 
 8 // Agregado Interface
 9 type Agregado interface {
10     CrearIterador() Iterador
11 }
12 
13 // Agregado Concreto
14 type Radio struct {
15     Estaciones []string
16 }
17 
18 func (r *Radio) CrearIterador() Iterador {
19     return &RadioIterador{radio: r}
20 }
21 
22 func (r *Radio) Registrar(estacion string) {
23     r.Estaciones = append(r.Estaciones, estacion)
24 }
25 
26 // Iterador Concreto
27 type RadioIterador struct {
28     radio  *Radio
29     indice int
30 }
31 
32 func (ri *RadioIterador) Valor() string {
33     return ri.radio.Estaciones[ri.indice]
34 }
35 
36 func (ri *RadioIterador) Siguiente() {
37     ri.indice++
38 }
39 
40 func (ri *RadioIterador) Anterior() {
41     ri.indice--
42 }

Se puede probar la implementación del patrón de la siguiente forma:

 1 radio := &Radio{}
 2 radio.Registrar("FM100")
 3 radio.Registrar("FM200")
 4 radio.Registrar("FM300")
 5 
 6 iterador := radio.CrearIterador()
 7 
 8 fmt.Printf("Escuhando la radio %s\n", iterador.Valor())
 9 
10 iterador.Siguiente()
11 fmt.Printf("Escuhando la radio %s\n", iterador.Valor())
12 
13 iterador.Siguiente()
14 fmt.Printf("Escuhando la radio %s\n", iterador.Valor())
15 
16 iterador.Anterior()
17 fmt.Printf("Escuhando nuevamente la radio %s\n", iterador.Valor())

Ejecutar código: https://play.golang.org/p/qpY_F7wrd6u

Código de ejemplo: https://github.com/danielspk/designpatternsingo/tree/master/patrones/comportamiento/iterator

Visitor

Propósito

Según el libro “Patrones de Diseño” [38] el patrón Visitor “representa una operación sobre los elementos de una estructura de objetos. Permite definir una nueva operación sin cambiar las clases de los elementos sobre los que opera”.

Estructura

Participantes

Implementación

Código de ejemplo

En este ejemplo queremos recrear un juego de rol en donde algunos personajes tengan superpoderes y otros armas de batalla.

Implementación:

 1 // Visitante
 2 type Visitante interface {
 3     VisitarSuperpoder(*ElementoSuperpoder)
 4     VisitarArma(*ElementoArma)
 5 }
 6 
 7 // Visitante Concreto
 8 type VisitanteNivel1 struct{}
 9 
10 func (v *VisitanteNivel1) VisitarSuperpoder(elementoSuperpoder *ElementoSuperpoder) {
11     elementoSuperpoder.Poder = "Rayo superpoderoso"
12 }
13 
14 func (v *VisitanteNivel1) VisitarArma(elementoArma *ElementoArma) {
15     elementoArma.Arma = "Espada de dos manos"
16 }
17 
18 // Visitante Concreto
19 type VisitanteNivel0 struct{}
20 
21 func (v *VisitanteNivel0) VisitarSuperpoder(elementoSuperpoder *ElementoSuperpoder) {
22     elementoSuperpoder.Poder = "Rayo simple"
23 }
24 
25 func (v *VisitanteNivel0) VisitarArma(elementoArma *ElementoArma) {
26     elementoArma.Arma = "Espada de una mano"
27 }
28 
29 // Elemento
30 type Elemento interface {
31     Aceptar(Visitante)
32 }
33 
34 // Elemento Concreto
35 type ElementoSuperpoder struct {
36     Poder string
37 }
38 
39 func (e *ElementoSuperpoder) Aceptar(visitante Visitante) {
40     visitante.VisitarSuperpoder(e)
41 }
42 
43 // Elemento Concreto
44 type ElementoArma struct {
45     Arma string
46 }
47 
48 func (e *ElementoArma) Aceptar(visitante Visitante) {
49     visitante.VisitarArma(e)
50 }

Se puede probar la implementación del patrón de la siguiente forma:

 1 // desde visitar
 2 elementoArma0 := &ElementoArma{}
 3 elementoSuperpoder0 := &ElementoSuperpoder{}
 4 
 5 visitanteNivel0 := &VisitanteNivel0{}
 6 visitanteNivel0.VisitarArma(elementoArma0)
 7 visitanteNivel0.VisitarSuperpoder(elementoSuperpoder0)
 8 
 9 fmt.Printf("El visitante Nivel 0 tiene la siguiente arma de batalla: %s\n", elementoArma0.A\
10 rma)
11 fmt.Printf("El visitante Nivel 0 tiene el siguiente superpoder: %s\n", elementoSuperpoder0.\
12 Poder)
13 
14 elementoArma1 := &ElementoArma{}
15 elementoSuperpoder1 := &ElementoSuperpoder{}
16 
17 visitanteNivel1 := &VisitanteNivel1{}
18 visitanteNivel1.VisitarArma(elementoArma1)    visitanteNivel1.VisitarSuperpoder(elementoSup\
19 erpoder1)
20 
21 fmt.Printf("El visitante Nivel 1 tiene la siguiente arma de batalla: %s\n", elementoArma1.A\
22 rma)
23 fmt.Printf("El visitante Nivel 1 tiene el siguiente superpoder: %s\n", elementoSuperpoder1.\
24 Poder)
25 
26 // desde aceptar
27 visitanteNivel0 = &VisitanteNivel0{}
28 elementoArma0 = &ElementoArma{}
29 elementoArma0.Aceptar(visitanteNivel0)
30 
31 fmt.Printf("El elemento Arma aceptada por un visitante Nivel 0 es: %s\n", elementoArma0.Arm\
32 a)

Ejecutar código: https://play.golang.org/p/WSPGvlwREuQ

Código de ejemplo: https://github.com/danielspk/designpatternsingo/tree/master/patrones/comportamiento/visitor

State

Propósito

Según el libro “Patrones de Diseño” [38] el patrón State “permite que un objeto modifique su comportamiento cada vez que cambie su estado interno. Parecerá que cambia la clase del objeto”.

También conocido como

Objects for states (Estados como Objetos)

Estructura

Participantes

Implementación

Código de ejemplo

En este ejemplo queremos escribir texto en base al estado de una botonera de estilos, pudiendo ser estos estados “negrita” o “cursiva”.

Implementación:

 1 // Interface Estado
 2 type Estado interface {
 3     Escribir(string) string
 4 }
 5 
 6 // Estado Concreto
 7 type EstadoNegrita struct{}
 8 
 9 func (en *EstadoNegrita) Escribir(texto string) string {
10     return "*" + texto + "*"
11 }
12 
13 // Estado Concreto
14 type EstadoCursiva struct{}
15 
16 func (ec *EstadoCursiva) Escribir(texto string) string {
17     return "_" + texto + "_"
18 }
19 
20 // Contexto
21 type EditorMarkdown struct {
22     estado Estado
23 }
24 
25 func (em *EditorMarkdown) SetEstado(estado Estado) {
26     em.estado = estado
27 }
28 
29 func (em *EditorMarkdown) Redactar(texto string) string {
30     if em.estado == nil {
31         return texto
32     }
33 
34     return em.estado.Escribir(texto)
35 }

Se puede probar la implementación del patrón de la siguiente forma:

1 editor := &EditorMarkdown{}
2 fmt.Printf("Texto redactado sin estado: %s\n", editor.Redactar("Lorem ipsum"))
3 
4 editor.SetEstado(&EstadoNegrita{})
5 fmt.Printf("Texto redactado en negrita: %s\n", editor.Redactar("Lorem ipsum"))
6 
7 editor.SetEstado(&EstadoCursiva{})
8 fmt.Printf("Texto redactado en cursiva: %s\n", editor.Redactar("Lorem ipsum"))

Ejecutar código: https://play.golang.org/p/KsYfTyLBDVI

Código de ejemplo: https://github.com/danielspk/designpatternsingo/tree/master/patrones/comportamiento/state

Mediator

Propósito

Según el libro “Patrones de Diseño” [38] el patrón Mediator “define un objeto que encapsula cómo interactúan una serie de objetos. Promueve un bajo acoplamiento al evitar que los objetos se refieran unos a otros explícitamente, y permite variar la interacción entre ellos de forma independiente”.

Estructura

Participantes

Implementación

Código de ejemplo

En este ejemplo queremos montar una sala de chat en donde los usuarios puedan comunicarse entre sí. La sala de chat actúa como mediador entre los usuarios.

Implementación:

 1 // Interface Mediador
 2 type Mediador interface {
 3     MostrarMensaje(Usuario, string)
 4 }
 5 
 6 // Mediador Concreto
 7 type ChatRoom struct{}
 8 
 9 func (cr *ChatRoom) MostrarMensaje(usuario Usuario, mensaje string) {
10     fmt.Printf("El mensaje de %s es: %s\n", usuario.GetNombre(), mensaje)
11 }
12 
13 // Interface Colega
14 type Usuario interface {
15     EnviarMensaje(string)
16     GetNombre() string
17 }
18 
19 // Colega Concreto
20 type UsuarioChat struct {
21     nombre   string
22     mediador Mediador
23 }
24 
25 func (u *UsuarioChat) GetNombre() string {
26     return u.nombre
27 }
28 
29 func (u *UsuarioChat) EnviarMensaje(mensaje string) {
30     u.mediador.MostrarMensaje(u, mensaje)
31 }

Se puede probar la implementación del patrón de la siguiente forma:

1 mediador := &ChatRoom{}
2 
3 usuarioA := &UsuarioChat{"Daniel", mediador}
4 usuarioB := &UsuarioChat{"Pedro", mediador}
5 
6 usuarioA.EnviarMensaje("Hola como estas?")
7 usuarioB.EnviarMensaje("Muy bien y vos?")

Ejecutar código: https://play.golang.org/p/PWO1HBJYjPx

Código de ejemplo: https://github.com/danielspk/designpatternsingo/tree/master/patrones/comportamiento/mediator

Observer

Propósito

Según el libro “Patrones de Diseño” [38] el patrón Observer “define una dependencia de uno-a-muchos entre objetos, de forma que cuando un objeto cambie de estado se notifique y se actualicen automáticamente todos los objetos que depende de él”.

También conocido como

Dependents (Dependientes), Publish-subscribe (Publicar-Suscribir)

Estructura

Participantes

Implementación

Código de ejemplo

En este ejemplo queremos que postulantes a empleos sean notificados cuando se creen ofertas laborales.

Implementación:

 1 // Interface Sujeto
 2 type Sujeto interface {
 3     Adquirir(Observador)
 4     notificar()
 5 }
 6 
 7 // Sujeto Concreto
 8 type AgenciaEmpleo struct {
 9     observadores []Observador
10 }
11 
12 func (ae *AgenciaEmpleo) Adquirir(observador Observador) {
13     ae.observadores = append(ae.observadores, observador)
14 }
15 
16 func (ae *AgenciaEmpleo) notificar(oferta string) {
17     for _, observador := range ae.observadores {
18         observador.Actualizar(oferta)
19     }
20 }
21 
22 func (ae *AgenciaEmpleo) IngresarOfertaLaboral(oferta string) {
23     ae.notificar(oferta)
24 }
25 
26 // Interface Observador
27 type Observador interface {
28     Actualizar(string)
29 }
30 
31 // Observador Concreto
32 type ObservadorEmpleo struct {
33     nombre string
34 }
35 
36 func (o *ObservadorEmpleo) Actualizar(oferta string) {
37     fmt.Printf("Hola %s, existe la siguiente oferta de empleo: %s\n", o.nombre, oferta)
38 }

Se puede probar la implementación del patrón de la siguiente forma:

 1 observadorA := &ObservadorEmpleo{"Juan"}
 2 observadorB := &ObservadorEmpleo{"Maria"}
 3 
 4 agencia := &AgenciaEmpleo{}
 5 agencia.Adquirir(observadorA)
 6 agencia.Adquirir(observadorB)
 7 
 8 agencia.IngresarOfertaLaboral("Programador JAVA Senior")
 9 fmt.Printf("\n")
10 agencia.IngresarOfertaLaboral("Programador C# Junior")

Ejecutar código: https://play.golang.org/p/7CAEfYjM1lr

Código de ejemplo: https://github.com/danielspk/designpatternsingo/tree/master/patrones/comportamiento/observer

Patrones Creacionales

Imagen - [49]

Según el libro “Patrones de Diseño” [38] los patrones de creación “abstraen el proceso de creación de instancias. Ayudan a hacer un sistema independiente de cómo se crean, se componen y se representan sus objetos. Un patrón de creación de clases usa la herencia para cambiar la clase de la instancia a crear, mientras que un patrón de creación de objetos delega la creación de la instancia en otro objeto.”

Esta definición de Gamma et al es un desafío de representar en Go ya que como hemos visto no existen ni clases, ni sus instancias, ni objetos y ni la herencia de clase. Sin embargo veremos como todo encaja en su lugar en cada patrón y cómo pueden implementarse en Go.

Concurrencia

Go es un lenguaje que permite la programación concurrente y los patrones de diseño creacionales asumen que se trabaja sobre un único hilo de ejecución.
Dada la cantidad de estrategias y patrones de concurrencia existentes, sería muy dificultoso detallar como pueden verse afectadas las variables transmitidas por canales en desarrollos concurrentes.
Sin embargo, se realizará una mención especial en la explicación del patrón Singleton dado que como su propósito es crear una única variable de un tipo de dato, será interesante ver que estrategia permite implementar el lenguaje Go para cumplir con el propósito del patrón.

Contenido

Singleton

Propósito

Según el libro “Patrones de Diseño” [38] el patrón Singleton “garantiza que una clase solo tenga una instancia, y proporciona un punto de acceso global a ella”.

Estructura

Participantes

Implementación

Código de ejemplo

Implementación:

 1 // Singleton
 2 type Singleton struct {
 3     Tiempo int64
 4 }
 5 
 6 // Creador "estático"
 7 var instancia *Singleton
 8 var once sync.Once
 9 
10 func GetInstancia() *Singleton {
11     once.Do(func() {
12         instancia = &Singleton{
13             time.Now().Unix(),
14         }
15     })
16 
17     return instancia
18 }

Se puede probar la implementación del patrón de la siguiente forma:

 1 fmt.Println("Todas las instancias Singleton tienen que tener el mismo número")
 2 
 3 fmt.Printf("Instancia Singleton: %d\n", GetInstancia().Tiempo)
 4 
 5 time.Sleep(1 * time.Second)
 6 
 7 fmt.Printf("Instancia Singleton: %d\n", GetInstancia().Tiempo)
 8 
 9 canalEspera := make(chan int64)
10 
11 go func() {
12     time.Sleep(1 * time.Second)
13 
14     canalEspera <- GetInstancia().Tiempo
15 }()
16 
17 fmt.Printf("Instancia Singleton: %d\n", <-canalEspera)

Ejecutar código: https://play.golang.org/p/Fae3WyvrdIf

Código de ejemplo: https://github.com/danielspk/designpatternsingo/tree/master/patrones/creacionales/singleton

Builder

Propósito

Según el libro “Patrones de Diseño” [38] el patrón Builder “separa la construcción de un objeto complejo de su representación, de forma que el mismo proceso de construcción pueda crear diferentes representaciones”.

Estructura

Participantes

Implementación

Código de ejemplo

En este ejemplo queremos que un local de comida pueda entregar distintos tipos de hamburguesas (simples y dobles) para lo que será necesario generar distintos constructores de hamburguesas.

Implementación:

 1 // Interface Constructor
 2 type Constructor interface {
 3     EstablecerTamanio()
 4     Construir() *Hamburguesa
 5 }
 6 
 7 // Constructor Concreto
 8 type ConstructorHamburguesaSimple struct {
 9     tamanio string
10 }
11 
12 func (chs *ConstructorHamburguesaSimple) EstablecerTamanio() {
13     chs.tamanio = "Simple"
14 }
15 
16 func (chs *ConstructorHamburguesaSimple) Construir() *Hamburguesa {
17     return &Hamburguesa{chs.tamanio}
18 }
19 
20 // Constructor Concreto
21 type ConstructorHamburguesaDoble struct {
22     tamanio string
23 }
24 
25 func (chd *ConstructorHamburguesaDoble) EstablecerTamanio() {
26     chd.tamanio = "Doble"
27 }
28 
29 func (chd *ConstructorHamburguesaDoble) Construir() *Hamburguesa {
30     return &Hamburguesa{chd.tamanio}
31 }
32 
33 // Producto
34 type Hamburguesa struct {
35     Tamanio string
36 }
37 
38 // Director
39 type LocalComida struct{}
40 
41 func (lc *LocalComida) ConstruirHamburguesa(constructor Constructor) *Hamburguesa {
42     constructor.EstablecerTamanio()
43 
44     return constructor.Construir()
45 }

Se puede probar la implementación del patrón de la siguiente forma:

1 localComida := &LocalComida{}
2 
3 hamburguesaA := localComida.ConstruirHamburguesa(&ConstructorHamburguesaSimple{})
4 hamburguesaB := localComida.ConstruirHamburguesa(&ConstructorHamburguesaDoble{})
5 
6 fmt.Printf("Se solicito una hamburguesa: %s\n", hamburguesaA.Tamanio)
7 fmt.Printf("Se solicito una hamburguesa: %s\n", hamburguesaB.Tamanio)

Ejecutar código: https://play.golang.org/p/5dPp1a1Yaw_F

Código de ejemplo: https://github.com/danielspk/designpatternsingo/tree/master/patrones/creacionales/builder

Factory Method

Propósito

Según el libro “Patrones de Diseño” [38] el patrón Factory Method “define una interfaz para crear un objeto, pero deja que sean las subclases quienes decidan qué clase instanciar. Permite que una clase delegue en sus subclases la creación de objetos”.

También conocido como

Virtual Constructor (Constructor Virtual)

Estructura

Participantes

Implementación

Código de ejemplo

En este ejemplo queremos contratar personas con diferentes perfiles profesionales. A medida que los postulantes lleguen a la oficina de recursos humanos serán entrevistados (construidos) por diferentes reclutadores especializados.

Implementación:

 1 // Interface Producto
 2 type Entrevistador interface {
 3     RealizarPreguntas()
 4 }
 5 
 6 // Producto Concreto
 7 type EntrevistadorIT struct{}
 8 
 9 func (e *EntrevistadorIT) RealizarPreguntas() {
10     fmt.Println("¿Nombre 5 patrones de diseño?")
11 }
12 
13 // Producto Concreto
14 type EntrevistadorFinanzas struct{}
15 
16 func (e *EntrevistadorFinanzas) RealizarPreguntas() {
17     fmt.Println("¿Cuál es la alicuota del IVA?")
18 }
19 
20 // Creador Interface
21 type RecursosHumanosInterface interface {
22     LlamarEntrevistador() Entrevistador
23 }
24 
25 // Creador Abstracto
26 type RecursosHumanos struct{}
27 
28 func (rh *RecursosHumanos) TomarEntrevista(self RecursosHumanosInterface) {
29     entrevistador := self.LlamarEntrevistador()
30     entrevistador.RealizarPreguntas()
31 }
32 
33 // Creador Concreto
34 type RecusosHumanosIT struct {
35     *RecursosHumanos
36 }
37 
38 func (rhi *RecusosHumanosIT) LlamarEntrevistador() Entrevistador {
39     return &EntrevistadorIT{}
40 }
41 
42 // Creador Concreto
43 type RecusosHumanosFinanzas struct {
44     *RecursosHumanos
45 }
46 
47 func (rhf *RecusosHumanosFinanzas) LlamarEntrevistador() Entrevistador {
48     return &EntrevistadorFinanzas{}
49 }

Se puede probar la implementación del patrón de la siguiente forma:

1 fmt.Println("El entrevisatador de IT pregunta:")
2 recursosHumanosIT := &RecusosHumanosIT{&RecursosHumanos{}}
3 recursosHumanosIT.TomarEntrevista(recursosHumanosIT)
4 
5 fmt.Println("\nEl entrevisatador de Finanzas pregunta:")
6 recursosHumanosFinanzas := &RecusosHumanosFinanzas{&RecursosHumanos{}}
7 recursosHumanosFinanzas.TomarEntrevista(recursosHumanosFinanzas)

Ejecutar código: https://play.golang.org/p/1szkQi-rVUf

Código de ejemplo: https://github.com/danielspk/designpatternsingo/tree/master/patrones/creacionales/factorymethod

Abstract Factory

Propósito

Según el libro “Patrones de Diseño” [38] el patrón Abstract Factory “proporciona una interfaz para crear familias de objetos relacionados o que dependen entre sí, sin especificar sus clases concretas”.

También conocido como

Kit

Estructura

Participantes

Implementación

Código de ejemplo

En este ejemplo queremos comprar distintos tipos de puertas de madera (madera o metal). Al realizar el pedido el local de venta debe encargar cada puerta a distintos fabricantes, ya que quien realiza la puerta de madera no la hace de metal y viceversa.

Implementación:

 1 // Producto Abstracto Interface
 2 type Puerta interface {
 3     VerMaterial() string
 4 }
 5 
 6 // Producto Concreto
 7 type PuertaMadera struct{}
 8 
 9 func (pm *PuertaMadera) VerMaterial() string {
10     return "Madera"
11 }
12 
13 // Producto Concreto
14 type PuertaMetal struct{}
15 
16 func (pm *PuertaMetal) VerMaterial() string {
17     return "Metal"
18 }
19 
20 // Fábrica Abstracta Interface
21 type FabricaPuerta interface {
22     ConstruirPuerta() Puerta
23 }
24 
25 // Fábrica Concreta
26 type FabricaPuertaMadera struct{}
27 
28 func (fpm *FabricaPuertaMadera) ConstruirPuerta() Puerta {
29     return &PuertaMadera{}
30 }
31 
32 // Fábrica Concreta
33 type FabricaPuertaMetal struct{}
34 
35 func (fpm *FabricaPuertaMetal) ConstruirPuerta() Puerta {
36     return &PuertaMetal{}
37 }

Se puede probar la implementación del patrón de la siguiente forma:

1 fabricaPuertaMadera := &FabricaPuertaMadera{}
2 puertaMadera := fabricaPuertaMadera.ConstruirPuerta()
3 fmt.Printf("Se construyo un puerta de: %s\n", puertaMadera.VerMaterial())
4 
5 fabricaPuertaMetal := &FabricaPuertaMetal{}
6 puertaMetal := fabricaPuertaMetal.ConstruirPuerta()
7 fmt.Printf("Se construyo un puerta de: %s\n", puertaMetal.VerMaterial())

Ejecutar código: https://play.golang.org/p/8yy8vp4cDD5

Código de ejemplo: https://github.com/danielspk/designpatternsingo/tree/master/patrones/creacionales/abstractfactory

Prototype

Propósito

Según el libro “Patrones de Diseño” [38] el patrón Prototype “especifica los tipos de objetos a crear por medio de una instancia prototípica, y crea nuevos objetos copiando dicho prototipo”.

Estructura

Participantes

Implementación

No se observan impedimentos y/o modificaciones de la estructura original del patrón para su implementación en Go.

Código de ejemplo

En este ejemplo queremos que un elemento químico sea capaz de clonarse a sí mismo indicando cuantas veces fue clonado.

Implementación:

 1 // Prototipo Interface
 2 type Prototipo interface {
 3     Clonar() *Elemento
 4 }
 5 
 6 // Prototipo Concreto
 7 type Elemento struct {
 8     Material string
 9     Copias   int
10 }
11 
12 func (e *Elemento) Clonar() *Elemento {
13     return &Elemento{
14         Material: e.Material,
15         Copias:   e.Copias + 1,
16     }
17 }

Se puede probar la implementación del patrón de la siguiente forma:

 1 elementoA := &Elemento{"Azufre", 1}
 2 
 3 elementoB := elementoA.Clonar()
 4 elementoB.Material = elementoB.Material + " (fortificado)"
 5 
 6 elementoC := elementoB.Clonar()
 7 
 8 fmt.Printf("El elemento A es de %s y se clono %d veces\n", elementoA.Material, elementoA.Co\
 9 pias)
10 fmt.Printf("El elemento B es de %s y se clono %d veces\n", elementoB.Material, elementoB.Co\
11 pias)
12 fmt.Printf("El elemento C es de %s y se clono %d veces\n", elementoC.Material, elementoC.Co\
13 pias)

Ejecutar código: https://play.golang.org/p/3OAK3-IzcTT

Código de ejemplo: https://github.com/danielspk/designpatternsingo/tree/master/patrones/creacionales/prototype

Patrones Estructurales

Imagen - [49]

Según el libro “Patrones de Diseño” [38] los patrones estructurales “se ocupan de cómo se combinan las clases y los objetos para formar estructuras más grandes. Los patrones estructurales de clases hacen uso de la herencia para componer interfaces o implementaciones.”

Contenido

Composite

Propósito

Según el libro “Patrones de Diseño” [38] el patrón Composite “compone objetos en estructuras de árbol para representar jerarquías de parte-todo. Permite que los clientes traten de manera uniforme a los objetos individuales y a los compuestos”.

Estructura

Participantes

Implementación

No se observan impedimentos y/o modificaciones de la estructura original del patrón para su implementación en Go.

Código de ejemplo

En este ejemplo queremos acceder a los salarios de los empleados de una gerencia tanto de forma individual como grupal. De esta forma la compañía podría analizar el impacto de futuros bonos de productividad tanto para una gerencia completa o para alguno de sus empleados.

Implementación:

 1 // Componente Interface
 2 type Empleado interface {
 3     ObtenerSalario() int
 4 }
 5 
 6 // Hoja
 7 type DesarrolladorSenior struct{}
 8 
 9 func (ds *DesarrolladorSenior) ObtenerSalario() int {
10     return 1000
11 }
12 
13 // Hoja
14 type DesarrolladorJunior struct{}
15 
16 func (dj *DesarrolladorJunior) ObtenerSalario() int {
17     return 750
18 }
19 
20 // Compuesto
21 type GerenciaIT struct {
22     empleados []Empleado
23 }
24 
25 func (g *GerenciaIT) AgregarEmpleado(empleado Empleado) {
26     g.empleados = append(g.empleados, empleado)
27 }
28 
29 func (g *GerenciaIT) ObtenerSalario() int {
30     sumaSalarios := 0
31 
32     for _, empleado := range g.empleados {
33         sumaSalarios = sumaSalarios + empleado.ObtenerSalario()
34     }
35 
36     return sumaSalarios
37 }

Se puede probar la implementación del patrón de la siguiente forma:

 1 empleadoA := &DesarrolladorJunior{}
 2 empleadoB := &DesarrolladorJunior{}
 3 empleadoC := &DesarrolladorSenior{}
 4 
 5 gerenciaIT := &GerenciaIT{}
 6 gerenciaIT.AgregarEmpleado(empleadoA)
 7 gerenciaIT.AgregarEmpleado(empleadoB)
 8 gerenciaIT.AgregarEmpleado(empleadoC)
 9 
10 fmt.Printf("El salario individual del desarrollador A es de $%d\n", empleadoA.ObtenerSalari\
11 o())
12 fmt.Printf("El salario individual del desarrollador B es de $%d\n", empleadoB.ObtenerSalari\
13 o())
14 fmt.Printf("El salario individual del desarrollador C es de $%d\n", empleadoC.ObtenerSalari\
15 o())
16 fmt.Printf("Los salarios de todos los desarrolladores de la Gerencia es de $%d\n", gerencia\
17 IT.ObtenerSalario())

Ejecutar código: https://play.golang.org/p/BR_zwXpOD0O

Código de ejemplo: https://github.com/danielspk/designpatternsingo/tree/master/patrones/estructurales/composite

Adapter

Propósito

Según el libro “Patrones de Diseño” [38] el patrón Adapter “convierte la interfaz de una clase en otra interfaz que es la que esperan los clientes. Permite que cooperen clases que de otra forma no podrían por tener interfaces incompatibles”.

También conocido como

Wrapper (Envoltorio)

Estructura

Participantes

Implementación

No se observan impedimentos y/o modificaciones de la estructura original del patrón para su implementación en Go.

Código de ejemplo

En este ejemplo queremos que un juego de RPG se pueda adaptar a un nuevo tipo de personaje (Magos) que no comparte las mismas características que los guerreros originales (Elfos). Para esto es necesario realizar un adaptador para que un Mago pueda atacar como un Elfo.

Implementación:

 1 // Objetivo
 2 type Gerrero interface {
 3     UsarArma() string
 4 }
 5 
 6 type Elfo struct{}
 7 
 8 func (e *Elfo) UsarArma() string {
 9     return "atacando con arco y flecha"
10 }
11 
12 // Adaptable
13 type GerreroMagico interface {
14     UsarMagia() string
15 }
16 
17 type Mago struct{}
18 
19 func (m *Mago) UsarMagia() string {
20     return "atacando con magia"
21 }
22 
23 // Adaptador
24 type MagoAdaptador struct {
25     gerrero GerreroMagico
26 }
27 
28 func (ma *MagoAdaptador) UsarArma() string {
29     return ma.gerrero.UsarMagia()
30 }
31 
32 // Cliente
33 type Jugador struct {
34     guerrero Gerrero
35 }
36 
37 func (j *Jugador) Atacar() string {
38     return j.guerrero.UsarArma()
39 }

Se puede probar la implementación del patrón de la siguiente forma:

1 jugadorA := &Jugador{&Elfo{}}
2 fmt.Printf("Jugador A: %s\n", jugadorA.Atacar())
3 
4 jugadorB := &Jugador{&MagoAdaptador{&Mago{}}}
5 fmt.Printf("Jugador B: %s\n", jugadorB.Atacar())

Ejecutar código: https://play.golang.org/p/60tlY8la04W

Código de ejemplo: https://github.com/danielspk/designpatternsingo/tree/master/patrones/estructurales/adapter

Bridge

Propósito

Según el libro “Patrones de Diseño” [38] el patrón Bridge “desacopla una abstracción de su implementación, de modo que ambas puedan variar de forma independiente”.

También conocido como

Handle/Body (Manejador/Cuerpo)

Estructura

Participantes

Implementación

Código de ejemplo

En este ejemplo queremos desacoplar el protocolo de conexión a internet que pueden implementar distintos dispositivos.

Implementación:

 1 // Abstraccion Interface
 2 type DispositivoInterface interface {
 3     ConectarInternet() string
 4     SetConexion(Conexion)
 5 }
 6 
 7 // Abstraccion Abstracta
 8 type Dispositivo struct {
 9     conexion Conexion
10 }
11 
12 func (d *Dispositivo) SetConexion(conexion Conexion) {
13     d.conexion = conexion
14 }
15 
16 // Abstraccion Refinada
17 type Telefono struct {
18     numero string
19     *Dispositivo
20 }
21 
22 func (t *Telefono) ConectarInternet() string {
23     return "Teléfono N° " + t.numero + " conectado a internet mediante " + t.conexion.Conec\
24 tar()
25 }
26 
27 // Abstraccion Refinada
28 type Tablet struct {
29     *Dispositivo
30 }
31 
32 func (t *Tablet) ConectarInternet() string {
33     return "Tablet conectada a internet mediante " + t.conexion.Conectar()
34 }
35 
36 // Implementador Interface
37 type Conexion interface {
38     Conectar() string
39 }
40 
41 // Implementador Concreto
42 type Red4G struct{}
43 
44 func (r *Red4G) Conectar() string {
45     return "4G"
46 }
47 
48 // Implementador Concreto
49 type RedWiFi struct{}
50 
51 func (r *RedWiFi) Conectar() string {
52     return "WiFi"
53 }

Se puede probar la implementación del patrón de la siguiente forma:

 1 telefonoA := &Telefono{"0115161", &Dispositivo{}}
 2 telefonoA.SetConexion(&Red4G{})
 3 fmt.Printf("%s\n", telefonoA.ConectarInternet())
 4 
 5 telefonoB := &Telefono{"0117854", &Dispositivo{}}
 6 telefonoB.SetConexion(&RedWiFi{})
 7 fmt.Printf("%s\n", telefonoB.ConectarInternet())
 8 
 9 tablet := &Tablet{&Dispositivo{}}
10 tablet.SetConexion(&RedWiFi{})
11 fmt.Printf("%s\n", tablet.ConectarInternet())

Ejecutar código: https://play.golang.org/p/PnQdNHLsrSc

Código de ejemplo: https://github.com/danielspk/designpatternsingo/tree/master/patrones/estructurales/bridge

Proxy

Propósito

Según el libro “Patrones de Diseño” [38] el patrón Proxy “proporciona un representante o sustituto de otro objeto para controlar el acceso a este”.

También conocido como

Surrogate (Sustituto)

Estructura

Participantes

Implementación

No se observan impedimentos y/o modificaciones de la estructura original del patrón para su implementación en Go.

Código de ejemplo

En este ejemplo queremos restringir los accesos a redes sociales en los navegadores de una escuela. Para esto realizaremos un proxy de protección.

Implementación:

 1 // Sujeto Interface
 2 type NavegadorInterface interface {
 3     Direccion(string) string
 4 }
 5 
 6 // Sujeto Real
 7 type Navegador struct{}
 8 
 9 func (n *Navegador) Direccion(url string) string {
10     return "Respuesta de la url " + url
11 }
12 
13 // Proxy
14 type NavegadorProxy struct {
15     navegador NavegadorInterface
16 }
17 
18 func (n *NavegadorProxy) Direccion(url string) string {
19     if url == "http://twitter.com" || url == "http://facebook.com" {
20         return "Acceso restringido a " + url
21     }
22 
23     return n.navegador.Direccion(url)
24 }

Se puede probar la implementación del patrón de la siguiente forma:

1 navegadorProxy := &NavegadorProxy{&Navegador{}}
2 
3 fmt.Printf("%s\n", navegadorProxy.Direccion("http://google.com"))
4 fmt.Printf("%s\n", navegadorProxy.Direccion("http://twitter.com"))
5 fmt.Printf("%s\n", navegadorProxy.Direccion("http://facebook.com"))

Ejecutar código: https://play.golang.org/p/7JSOE4GYByc

Código de ejemplo: https://github.com/danielspk/designpatternsingo/tree/master/patrones/estructurales/proxy

Decorator

Propósito

Según el libro “Patrones de Diseño” [38] el patrón Decorator “asigna responsabilidades adicionales a un objeto dinámicamente, proporcionando una alternativa flexible a la herencia para extender la funcionalidad”.

También conocido como

Wrapper (Envoltorio)

Estructura

Participantes

Implementación

No se observan impedimentos y/o modificaciones de la estructura original del patrón para su implementación en Go.

Código de ejemplo

En este ejemplo queremos agregarle dinámicamente ingredientes adicionales a un café que por defecto viene simple.

Implementación:

 1 // Componente Interface
 2 type CafeInterface interface {
 3     GetCosto() int
 4     GetDetalle() string
 5 }
 6 
 7 // Componente Concreto
 8 type Cafe struct{}
 9 
10 func (c *Cafe) GetCosto() int {
11     return 30
12 }
13 
14 func (c *Cafe) GetDetalle() string {
15     return "Cafe"
16 }
17 
18 // Decorador
19 type CafeDecorador struct {
20     CafeInterface
21 }
22 
23 func (cd *CafeDecorador) GetCosto() int {
24     return cd.CafeInterface.GetCosto()
25 }
26 
27 func (cd *CafeDecorador) GetDetalle() string {
28     return cd.CafeInterface.GetDetalle()
29 }
30 
31 // Decorador Concreto
32 type CafeConCrema struct {
33     *CafeDecorador
34 }
35 
36 func (ccc *CafeConCrema) GetCosto() int {
37     return ccc.CafeDecorador.GetCosto() + 15
38 }
39 
40 func (ccc *CafeConCrema) GetDetalle() string {
41     return ccc.CafeDecorador.GetDetalle() + " con crema"
42 }
43 
44 // Decorador Concreto
45 type CafeConCanela struct {
46     *CafeDecorador
47 }
48 
49 func (ccc *CafeConCanela) GetCosto() int {
50     return ccc.CafeDecorador.GetCosto() + 10
51 }
52 
53 func (ccc *CafeConCanela) GetDetalle() string {
54     return ccc.CafeDecorador.GetDetalle() + " con canela"
55 }

Se puede probar la implementación del patrón de la siguiente forma:

 1 cafe := &Cafe{}
 2 fmt.Printf("Detalle: %s - Importe $%d\n", cafe.GetDetalle(), cafe.GetCosto())
 3 
 4 cafeConCrema := &CafeConCrema{&CafeDecorador{cafe}}
 5 fmt.Printf("Detalle: %s - Importe $%d\n", cafeConCrema.GetDetalle(), cafeConCrema.GetCosto(\
 6 ))
 7 
 8 cafeConCremaConCanela := &CafeConCanela{&CafeDecorador{cafeConCrema}}
 9 fmt.Printf("Detalle: %s - Importe $%d\n", cafeConCremaConCanela.GetDetalle(), cafeConCremaC\
10 onCanela.GetCosto())

Ejecutar código: https://play.golang.org/p/62xDpf7XUv_m

Código de ejemplo: https://github.com/danielspk/designpatternsingo/tree/master/patrones/estructurales/decorator

Facade

Propósito

Según el libro “Patrones de Diseño” [38] el patrón Facade “proporciona una interfaz unificada para un conjunto de interfaces de un subsistema. Define una interfaz de alto nivel que hace que el subsistema sea más fácil de usar”.

Estructura

Participantes

Implementación

No se observan impedimentos y/o modificaciones de la estructura original del patrón para su implementación en Go.

Código de ejemplo

En este ejemplo queremos que un sistema de agencia de viajes pueda interactuar con otros subsistemas: buscador de hoteles y buscador de pasajes aéreos.

Implementación:

 1 // Subsistema
 2 type BuscadorHotel struct{}
 3 
 4 func (bh *BuscadorHotel) BuscarHabitacion(entrada string, salida string) []string {
 5     return []string{
 6         "Hotel A con Habitación Classic - Entrada " + entrada + ", Salida " + salida + " - \
 7 $500.00",
 8         "Hotel B con Habitación Deluxe - Entrada " + entrada + ", Salida " + salida + " - $\
 9 750.00",
10     }
11 }
12 
13 // Subsistema
14 type BuscadorAvion struct{}
15 
16 func (ba *BuscadorAvion) BuscarPasaje(salida string, regreso string) []string {
17     return []string{
18         "Aerolinea A - Clase Turista - Salida " + salida + ", Regreso " + regreso + " - $24\
19 00.00",
20         "Aerolinea A - Clase Ejecutiva - Salida " + salida + ", Regreso " + regreso + " - $\
21 3200.00",
22         "Aerolinea B - Clase Ejecutiva - Salida " + salida + ", Regreso " + regreso + " - $\
23 3800.00",
24     }
25 }
26 
27 // Fachada
28 type AgenciaViaje struct{}
29 
30 func (av *AgenciaViaje) BuscarPaquete(desde string, hasta string) string {
31     buscadorHoteles := &BuscadorHotel{}
32     buscadorAvion := &BuscadorAvion{}
33 
34     resultadosHabitaciones := buscadorHoteles.BuscarHabitacion(desde, hasta)
35     resultadosPasajes := buscadorAvion.BuscarPasaje(desde, hasta)
36 
37     respuesta := "Hoteles disponibles:\n"
38 
39     for _, habitacion := range resultadosHabitaciones {
40         respuesta = respuesta + " - " + habitacion + "\n"
41     }
42 
43     respuesta = respuesta + "\nAerolineas disponibles:\n"
44 
45     for _, pajase := range resultadosPasajes {
46         respuesta = respuesta + " - " + pajase + "\n"
47     }
48 
49     return respuesta
50 }

Se puede probar la implementación del patrón de la siguiente forma:

1 agencia := &AgenciaViaje{}
2 
3 fmt.Printf("%s\n", agencia.BuscarPaquete("2018-12-01", "2018-12-08"))

Ejecutar código: https://play.golang.org/p/JM-ZC-pmaxu

Código de ejemplo: https://github.com/danielspk/designpatternsingo/tree/master/patrones/estructurales/facade

Flyweight

Propósito

Según el libro “Patrones de Diseño” [38] el patrón Flyweight “usa comportamiento para permitir un gran número de objetos de grano fino de forma eficiente”.

Estructura

Participantes

Implementación

No se observan impedimentos y/o modificaciones de la estructura original del patrón para su implementación en Go.

Código de ejemplo

En este ejemplo queremos que nuestro sistema sea capaz de dibujar controles en la pantalla haciendo un uso eficiente de los recursos, ya que existen controles (los botones) que pueden ser reutilizados.

Implementación:

 1 // Flyweight Interface
 2 type Control interface {
 3     Dibujar(x string, y string) string
 4     GetReferencia() string
 5 }
 6 
 7 // Flyweight Concreto
 8 type Boton struct {
 9     referencia string
10 }
11 
12 func (b *Boton) Dibujar(x string, y string) string {
13     return "Dibujando Botón #" + b.referencia + " en ejes " + x + ", " + y
14 }
15 
16 func (b *Boton) GetReferencia() string {
17     return b.referencia
18 }
19 
20 // Flyweight Concreto No Compartido
21 type CampoPassword struct{}
22 
23 func (cp *CampoPassword) Dibujar(x string, y string) string {
24     return "Dibujando Campo de Password en ejes " + x + ", " + y
25 }
26 
27 func (cp *CampoPassword) GetReferencia() string {
28     return ""
29 }
30 
31 // Fabrica Flyweight
32 type PantallaFabrica struct {
33     controles []Control
34 }
35 
36 func (pb *PantallaFabrica) ObtenerControl(tipo string, referencia string, x string, y strin\
37 g) string {
38     for _, control := range pb.controles {
39         if control.GetReferencia() == referencia {
40             return control.Dibujar(x, y) + " || <control recuperado>"
41         }
42     }
43 
44     if tipo == "BOTON" {
45         control := &Boton{referencia}
46 
47         pb.controles = append(pb.controles, control)
48 
49         return control.Dibujar(x, y)
50     }
51 
52     if tipo == "PASSWORD" {
53         control := &CampoPassword{}
54 
55         return control.Dibujar(x, y)
56     }
57 
58     return ""
59 }

Se puede probar la implementación del patrón de la siguiente forma:

1 pantalla := &PantallaFabrica{}
2 
3 fmt.Printf("%s\n", pantalla.ObtenerControl("BOTON", "BTN1", "100", "300"))
4 fmt.Printf("%s\n", pantalla.ObtenerControl("BOTON", "BTN2", "200", "300"))
5 fmt.Printf("%s\n", pantalla.ObtenerControl("BOTON", "BTN3", "300", "300"))
6 fmt.Printf("%s\n", pantalla.ObtenerControl("PASSWORD", "PWD1", "500", "300"))
7 fmt.Printf("%s\n", pantalla.ObtenerControl("BOTON", "BTN1", "400", "300"))

Ejecutar código: https://play.golang.org/p/o1TA4FcaAmD

Código de ejemplo: https://github.com/danielspk/designpatternsingo/tree/master/patrones/estructurales/flyweight

Parte III

Conclusiones y motivación de la publicación

Conclusiones

Go es un lenguaje que en muchos aspectos parece “retrasar” o da la sensación de “antiguo”. Esta visión puede ser correcta si se lo compara por ejemplo con la forma en la que programaríamos en Java. Esto es justamente lo que se debe evitar: “la comparación”. Go nace con una filosofía bien clara que todo desarrollador de Go debería conocer.

Dicho esto, tal como se pudo demostrar, es factible implementar los 23 Patrones de Diseño GoF en Go sin grandes modificaciones de las estructuras originales.

Su aplicación no es una simple traducción literal de un lenguaje a otro - como he visto muchas veces - sino que el uso de estos patrones realmente pueden aportar coherencia y valor al software desarrollado en Go.

Hemos visto que Go no es realmente un lenguaje orientado a objetos, sin embargo también se pudo demostrar que sus características permiten programar aplicando prácticas y patrones propios de la programación orientada a objetos.

Dicho esto, creo que es muy importante destacar que cada lenguaje de programación nace con un propósito definido, y que si bien las experiencias previas que tenemos como desarrolladores de software nos pueden facilitar el aprendizaje de nuevos lenguajes de programación, es vital comprender cuales son las recomendaciones, usos, estilos y características propias del lenguaje que estamos aprendiendo para no cometer el error común de querer hacer las cosas de igual manera como las haríamos en otros lenguajes.

Considero que Go es un excelente lenguaje para realizar trabajos concurrentes. Este tipo de programación hace que tengamos que repensar como podemos reutilizar soluciones ante problemas conocidos. Por tal motivo, y haciendo alusión a lo antes comentado, considero que un desarrollador de Go debería invertir más tiempo en aprender como aplicar patrones de concurrencia que patrones de diseño.

Estoy seguro de que todo lo visto en esta publicación es de valor para el desarrollador de Go, pero me gustaría cerrar esta conclusión con la siguiente recomendación:

Acerca De

Motivación

Esta publicación fue motivada como trabajo final de mi Posgrado de Especialización en Ingeniería del Software que curse durante el año 2017 en la UCA - Pontificia Universidad Católica Argentina [53].

Imagen - [53]

A lo largo de los últimos 10 años vengo desarrollando software principalmente orientado a objetos. En uno de mis últimos proyectos debimos analizar un cambio en el lenguaje de programación que utilizábamos porque uno de los principales atributos de calidad que debía cumplir el nuevo software era la eficiencia; por esto era fundamental que el software fuera lo más rápido posible. Si bien es un análisis muy pobre el analizar que tan rápido puede ser un software únicamente mirando con que lenguaje de programación es desarrollado, esta era una oportunidad que como equipo teníamos para explorar nuevos lenguajes. Luego de varias pruebas y discusiones quedaron dos candidatos finales: Go (https://golang.org/) y Rust (https://www.rust-lang.org/en-US/). La paradoja de esta historia es que al final el proyecto se terminó realizando en Node.js (https://nodejs.org/en/). Las razones escapan a esta publicación, sin embargo una de ellas tuvo un fuerte impacto es esa decisión: “cómo transferir el conocimiento orientado a objetos que posee el grupo de desarrolladores a estos lenguajes que no son puramente orientados a objetos, o no de igual manera como lo conocen los desarrolladores de Java, C#, PHP, etc”. Anteriormente decía que la elección de Node.js fue una paradoja porque quien alguna vez ha desarrollado en Javascript sabrá las limitaciones que posee su orientación a objetos y de las grandes discusiones que hay en la comunidad sobre si Javascript es realmente un lenguaje orientado a objetos u orientado a prototipos. Esta es la razón principal de esta publicación, la de poder ayudar a un futuro equipo de tecnología a que puedan visualizar como es la Programación Orientada a Objetos en Go partiendo de los ya clásicos Patrones de Diseño GoF.

El trabajo

El trabajo presentado se denomina:

“Una perspectiva a la Programación Orientada a Objetos en Go en base a los Patrones de Diseño GoF”

El mismo se presenta como:

“El objetivo principal del presente trabajo será mostrar cómo pueden aplicarse los Patrones de Diseño GoF en un lenguaje de programación que no es completamente orientado a objetos. Para esto se utilizará como referencia el lenguaje de programación Go. Su propósito principal será servir de material de referencia para desarrolladores Go que deseen aplicar los patrones de Diseño GoF. Se analizará la viabilidad, los ajustes, y las adaptaciones necesarias para implementar los Patrones de Diseño en base a las características del lenguaje de programación Go. Para esto, previamente se abordarán y explicarán los atributos orientados a objetos que posee el lenguaje de programación Go. Adicionalmente el trabajo se publicará como e-book online mediante la realización de una página web, junto a ejemplos auto ejecutables y un repositorio público con todo el código fuente, ejemplos y diagramas desarrollados.”

Porqué Go

Al momento de tomar la decisión sobre cuál de los dos lenguajes antes mencionados (Go, Rust) iba a basar mi trabajo, decidí hacer una pequeño análisis sobre la popularidad de ambos lenguajes en los últimos años. Para esto tome como referencia el índice de popularidad de los lenguajes de programación [51] de la Empresa Tiobe (https://www.tiobe.com).

Tiobe es una empresa especializada en la evaluación y el seguimiento de la calidad del software que publica mensualmente un índice [51] de popularidad de lenguajes de programación. Este índice es conformado en base a la cantidad de resultados que arrojan varios motores de búsqueda. Los resultados deben cumplir ciertos requerimientos para calificar para el índice, por ejemplo el lenguaje de programación debe tener su propia entrada en Wikipedia, ser completo (poder simular una máquina de Turing) y tener al menor 5.000 visitas en búsquedas aplicadas desde Google. La empresa también utiliza filtros para evitar falsos positivos y otras reglas para valorar la calificación del resultado.

El proceso de como Tiobe confecciona el índice se encuentra bien detallado en el siguiente link: https://www.tiobe.com/tiobe-index/programming-languages-definition/

Basándome en el índice antes mencionado realicé una comparación entre ambos lenguajes y Go, tanto históricamente como actualmente, tiene mayor popularidad respecto de Rust.

Como se puede apreciar en la siguiente gráfica (Mayo/2018) Go obtuvo su mejor ubicación dentro del ranking en Julio de 2017 al ubicarse entre los primeros 10 lenguajes de programación. Desde mediados de 2016 a mediados de 2017 tuvo su mayor pico de popularidad, con un marcado descenso actualmente.

Actualmente (Mayo/2018) Go se ubica en la 14ª posición:

Por el contrario Rust se ubica actualmente (Mayo/2018) en la 91ª posición en una clara caída dentro del ranking. Si bien por su posición actual sus estadísticas ya no se publican dentro del índice Tiobe, al momento de su evaluación estaba dentro del ranking de los 20ª a 50ª.

Existen otros sitios especializados en medir la popularidad de los lenguajes de programación como http://pypl.github.io/PYPL.html, y http://redmonk.com/sogrady/category/programming-languages/, pero Tiobe es a mi entender el que más criterios de evaluación y filtros detalla para su ranking.

En conclusión, el historial de popularidad de Go respecto de Rust fue lo que definió su selección para la publicación de este trabajo.

El camino recorrido

Antes de formalizar la idea final de esta publicación me definí dos objetivos para evaluar la factibilidad de su realización:

Mi primer objetivo fue muy fácil de corroborar. Si bien la inexistencia de “clases” en Go es un condicionante en la forma en que se expresan y documentan los patrones de diseño GoF; la semántica de tipos de datos que implementan comportamientos fue, a mi entender, lo suficientemente análoga a las clases tradicionales. Semánticamente los patrones son implementables en Go - con su sintaxis particular -.

El segundo objetivo fue más difícil de llevar a cabo: ¿Qué significa que la implementación de un patrón de diseño GoF aporte valor al software?. La mejor manera que encontré de dar respuesta a esta pregunta fue demostrar todo lo contrario: ¿Cómo una implementación semántica de un patrón de diseño GoF no aporta valor alguno al software?.

El siguiente código de Javascript ES5 implementa el patrón de comportamiento Strategy:

 1 var Transportista = function() {
 2     this.empresa = "";
 3 };
 4 
 5 Transportista.prototype = {
 6     setStrategy: function(empresa) {
 7         this.empresa = empresa;
 8     },
 9     calcularTasa: function() {
10         return this.empresa.calcularTasa();
11     }
12 };
13 
14 var EmpresaA = function() {
15     this.calcularTasa = function() {
16         return 10;
17     }
18 };
19 
20 var EmpresaB = function() {
21     this.calcularTasa = function() {
22         return 15;
23     }
24 };
25 
26 var transportista = new Transportista;
27 
28 transportista.setStrategy(new EmpresaA())
29 console.log("Tasa Empresa A: " + transportista.calcularTasa());
30 
31 transportista.setStrategy(new EmpresaB())
32 console.log("Tasa Empresa B: " + transportista.calcularTasa());

Semánticamente la aplicación del patrón es correcta. El contexto es el Transportista y las estrategias son las Empresas. Sin embargo esta implementación es exclusivamente semántica, porque el lenguaje:

Como se puede ver la programación orientada a objetos en Javascript ES5 es muy limitada, y si bien semánticamente puede implementarse los patrones de diseño GoF, aportan valor al software sólo en cuanto a su funcionamiento final pero no así respecto de la reutilización de código.

En contrapartida, en Go, la aplicación de patrones de diseño GoF aportan valor pleno al software como en otros lenguajes de programación orientados a objetos, ya que el lenguaje tiene características y comportamientos comparables.

En conclusión mis objetivos iniciales fueron cumplimentados y los siguientes pasos fueron:

Las herramientas utilizadas

Para esta publicación se priorizó la utilización de herramientas que cumplieran alguna de las siguientes premisas:

Las herramientas utilizadas fueron:

La motivación de la elección de estas, y no otras herramientas, fue la siguiente:

Agradecimientos

Quisiera concluir agradeciendo a:

Recursos de Interés

Imagen - [49]

A continuación se exponen los recursos de interés que fueron utilizados como referencia para el desarrollo de esta publicación.

Golang

[1] - Web oficial
[2] - Documentación oficial
[3] - Preguntas frecuentes oficiales
[4] - Consola interactiva
[5] - Blog oficial

Artículos Web

[6] - Let’s Go: Object-Oriented Programming in Golang
[7] - SOLID Go Design
[8] - Is Go An Object Oriented Language?
[9] - Object Orientation In Go
[10] - Design Patterns for Humans!
[11] - Java Design Patterns - Example Tutorial
[12] - Design patterns implemented in Java
[13] - Design Patterns in Java Tutorial
[14] - Why extends is evil
[15] - Inheritance vs Composition
[16] - Go is not good
[17] - Go at Google: Language Design in the Service of Software Engineering

Papers

[18] - Publicaciones académicas
[19] - GoHotDraw: Evaluating the Go Programming Language with Design Patterns - Frank Schmager, Nicholas Cameron, James Noble

Libros

[20] - Karl Seguin, “The Litte Go Book”, 2014
[21] - Hemant Jain, “Data Structures & Algorithms In Go” - WOW, 2017
[22] - Matt Aimoneaatti, “Go Bootcamp” - 2014
[23] - Aaron Torres, “Go Cookbook” - Packt, 2017
[24] - Mario Castro Contreras, “Go Design Patterns” - Packt, 2017
[25] - Vladimir Vivien, Mario Castro Contreras, Mat Ryer, “Go: Design Patterns for Real-World Projects” - Packt, 2017
[26] - Matt Butcher, Matt Farina, “Go In Practice” - Manning, 2016
[27] - Mat Ryer, “Go Programming Blueprints Second Edition” - Packt, 2016
[28] - Shiju Varghese, “Go Recipes” - Apress, 2016
[29] - Mihalis Tsoukalos, “Go Systems Programming” - Packt, 2017
[30] - Sau Sheong Chang, “Go Web Programming” - Manning, 2016
[31] - Vladimir Vivien, “Learning Go Programming” - Packt, 2016
[32] - Nathan Zozyra, “Learning Go Web Development” - Packt, 2016
[33] - Daniel Whitenack, “Machine Learning With Go” - Packt, 2017
[34] - Jan Newmarch, “Network Programming with Go” - Apress, 2017
[35] - Nathan Zozyra, Mat Ryer, “Go: Building Web Applications” - Packt, 2016
[36] - Mark Summerfield, “Programming in Go” - Addison-Wesley Professional, 2012
[37] - Thorsten Ball, “Writing an Interpreter in Go” - 2016
[38] - Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides, “Patrones de Diseño” - Addison Wesley, 2002
[39] - Junade Ali, “Mastering PHP Design Patterns” - Packt, 2016
[40] - Robert C. Martin, “Agile Software Development, Principles, Patterns, and Practices” - Alan Apt Series, 2002
[41] - Caleb Doxsey, “Introducing Go” - O’Reilly, 2016

Presentaciones

[42] - Go for Javaneros
[43] - Rob Pike - Simplicity is Complicated
[44] - Rob Pike - Go Proverbs
[45] - Francesc Campoy - Google Understanding Go Interfaces
[46] - Dave Cheney - SOLID Go Design
[47] - William Kennedy - Golang OOP - Composition in Go

Otros Recursos

[48] - Librerías, paquetes y framewoks
[49] - Gophers oficiales
[50] - Gophers libres
[51] - Índice Tiobe
[52] - Go logos
[53] - UCA - Pontificia Universidad Católica Argentina
[54] - Python - Glosario

Glosario

Clase

Las clases propias de la programación orientada a objetos se definirán como tipos de datos o tipos.

Objeto

Los objetos propios de la programación orientada a objetos se definirán como variables.

Método

Los métodos de clases propios de la programación orientada a objetos se definirán como comportamientos de un tipo o simplemente como método (de tipo).

Propiedad

Las propiedades de clases propias de la programación orientada a objetos se definirán como atributos de una estructura.