Está en la página 1de 126

Patrones de Diseño en Go

Una perspectiva a la Programación Orientada a Objetos


en Go en base a los Patrones de Diseño GoF

Daniel M. Spiridione
Este libro está a la venta en http://leanpub.com/designpatternsingo

Esta versión se publicó en 2020-08-21

Éste es un libro de Leanpub. Leanpub anima a los autores y publicadoras con el proceso de
publicación. Lean Publishing es el acto de publicar un libro en progreso usando herramientas
sencillas y muchas iteraciones para obtener retroalimentación del lector hasta conseguir el libro
adecuado.

© 2020 Daniel M. Spiridione


Índice general

Sobre esta publicación . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1

Introducción . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
Organización . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2

Parte I . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
Sobre Go . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4

POO en Go . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
Objetos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
Herencia / Composición . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
S.O.L.I.D . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23

Parte II . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
Patrones de Diseño . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31

GoF . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32

Patrones de Comportamiento . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37
Strategy . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38
Chain of Responsibility . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41
Command . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44
Template Method . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47
Memento . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51
Interpreter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54
Iterator . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57
Visitor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60
State . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 64
Mediator . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 67
Observer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 70

Patrones Creacionales . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73
Singleton . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74
Builder . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 76
Factory Method . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 79
ÍNDICE GENERAL

Abstract Factory . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 82
Prototype . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 85

Patrones Estructurales . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87
Composite . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 88
Adapter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91
Bridge . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 94
Proxy . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 97
Decorator . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 100
Facade . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 103
Flyweight . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 106

Parte III . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 109


Conclusiones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 110

Acerca De . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 111

Recursos de Interés . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 118

Glosario . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 122
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:
http://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: 20 de Agosto de 2020 a las 20:55 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.

Figura: Logo oficial del Lenguaje Go ¹

¹Fuente: https://golang.org/s/logos
Parte I
Presentación del Lenguaje Go y sus características Orientadas
a Objetos
Sobre Go

Figura: Gopher - Mascota del Lenguaje Go ²

“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.

Figura: Gopher - Mascota del Lenguaje Go ³

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.
²Fuente: https://golang.org/doc/gopher
³Fuente: https://github.com/golang-samples/gopher-vector
Sobre Go 5

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:

• los largos tiempos de compilación


• las dependencias no controladas
• las variaciones de uso de subconjuntos diferentes de un lenguaje por parte de los desarro-
lladores
• los códigos difíciles de leer
• los códigos mal documentados

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

Se recomienda utilizar el término Golang al realizar búsquedas en internet referidas al


lenguaje.

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.
Sobre Go 6

Figura: Características del Lenguaje Go ⁴

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:

• compilado,
• concurrente,
• de tipado estático,
• con recolector de basura,
• con uso de punteros - pero sin aritmética
• con cortos tiempos de compilación

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
⁴Fuente: Vladimir Vivien, Mario Castro Contreras, Mat Ryer, “Go: Design Patterns for Real-World Projects” - Packt, 2017
Sobre Go 7

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 caracte-
rí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:

• genéricos
• manejo de excepciones
• sobrecarga de operadores

En el siguiente link puede acceder a una serie de artículos, de diferentes autores,


destinados a detallar y debatir sobre las debilidades que encuentran en el lenguaje Go -
https://github.com/ksimka/go-is-not-good [16]. Varios de los argumentos son bastantes
graciosos desde mi punto de vista: por ejemplo algunos critican la mascota del lenguaje,
o se quejan de que es un lenguaje compilado, o que su sintaxis es del estilo de C.

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
Sobre Go 8

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:

• No se comunique compartiendo memoria, comparta memoria comunicándose.


• La concurrencia no es paralelismo.
• Cuanto más grande es la interfaz, más débil es la abstracción.
• Haz que el valor cero sea útil.
• interface{} no dice nada.
• El estilo de gofmt no es el favorito de nadie, sin embargo, gofmt es el favorito de todos.
• Copiar un poco es mejor que una pequeña dependencia.
• Claro es mejor que inteligente.
• La reflexión nunca es clara.
• Los errores son valores
• No solo revise los errores, trátelos con gracia.

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


POO en Go

Figura: Gopher - Mascota del Lenguaje Go ⁵

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

Contenido
• Objetos en Go
• Herencia vs Composición en Go
• Principios S.O.L.I.D en Go

⁵Fuente: https://golang.org/doc/gopher
POO en Go 10

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.

En ES6 se incorporan las clases en Javascript aunque con un soporte muy limitado en
comparación con otros lenguajes orientados a objetos clásicos.

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:

• Herencia de clase
– Simple
* Scala
* Smalltalk
– Múltiple
* Eifell
* C++
• Herencia de Interfaces
– Explícitas
* C++ (mediante clases abstractas y métodos virtuales)
* Scala (mediante herencia simple de clase y/o traits)
POO en Go 11

* Eifell (mediante herencia simple/múltiple de clase)


– Implícitas
* Smalltalk
• Rasgos (Traits)
– Permite
* Scala
* Smalltalk
* C++ (mediante templates)
– No Permite
* Eiffel
• Sobrecarga de Métodos
– Permite
* Scala
* C++
– No Permite
* Smalltalk
* Eiffel

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:
POO en Go 12

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)
POO en Go 13

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


POO en Go 14

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.
POO en Go 15

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
POO en Go 16

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”.
POO en Go 17

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
• Herencia múltiple
• Polimorfismo
• Composición de objetos

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.

Esto en cierta medida es más similar al concepto de herencia múltiple que al de simple.

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


POO en Go 18

¿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 de-
masiada 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
POO en Go 19

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:

• Reutilización de código: todo código definido en la clase Fruta puede ser reutilizado por
cualquiera de sus subclases.
• Polimorfismo: una variable de tipo Fruta puede contener una referencia a un objeto de tipo
superclase o cualquiera de sus subclases.
• Enlace dinámico: en tiempo de ejecución se decidirá que método invocar en función del
tipo de clase del objeto.
• Facilidad para agregar una nueva subclase: simplemente se debe agregar una subclase
que heredé de la superclase.

Contras:

• Equipaje adicional: se heredan todos los métodos y propiedades de la superclase aunque


no se deseen todos ellos.
• Alto Acoplamiento: La subclase y superclase están fuertemente conectadas. Un cambio en
la superclase puede impactar negativamente en la subclase.
• Débil Encapsulamiento: si cambia la firma de un método en la superclase, hasta que la
subclase no lo modifique el código no funcionará/compilará.

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 }
POO en Go 20

Pros:

• Bajo Acoplamiento: Cualquier cambio en la clase Fruta no afecta a la clase Manzana.


Incluso si se agrega un método en la clase Fruta con la misma firma de uno de la clase
Manzana no afecta a esta última.
• Reutilización de Código: Se puede lograr de igual forma que con la herencia, aunque hay
que llamar explícitamente al código que necesita ser reutilizado (Nota: esto último no es
necesario en Go).
• Encapsulamiento más fuerte: en el ejemplo, si se cambia la firma del método pelar() en la
clase Fruta, no hay necesidad de cambiar nada en la clase Manzana.

Contras:

• Es más difícil agregar una nueva clase: sintácticamente requiere de más código.
• Costo de rendimiento: el método explícito de reenvío de llamadas tiene un costo de
rendimiento en comparación con la invocación directa de la herencia.

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
POO en Go 21

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:

• la composición cuando exista un comportamiento que deba ser compartido entre tipos de
datos
• mediante interfaces cuando se deba asegurar que una estructura es parte de una relación
es-un y/o requiera implementar ciertos comportamientos.

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


POO en Go 22

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

Puede verse el uso de esta estrategia en el patrón Template Method.

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.
POO en Go 23

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

• S: (SRP - Single responsibility principle) Principio de responsabilidad única.


• O: (OCP - Open closed principle) Principio abierto cerrado.
• L: (LSP - Liskov substitution principle) Principio de substitución de Liskov.
• I: (ISP - Interface segregation principle) Principio de segregación de la interfaz.
• D: (DIP - Dependency inversion principle) Principio de inversión de la dependencia.

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

Una clase debe tener una, y sólo una, razón para cambiar, lo que significa que una clase
debe tener un solo trabajo.

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 encapsu-
lada 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 responsa-
bilidades 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 }
POO en Go 24

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

Los objetos o entidades deberían estar abiertos para la extensión, pero cerrados para su
modificación.
POO en Go 25

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

Cada clase que herede de otra debe poder utilizarse como su clase padre sin necesidad
de conocer las diferencias que pudieran existir entre ellas.

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:
POO en Go 26

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 }
POO en Go 27

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

Nunca se debe obligar a un cliente a implementar una interfaz que no utilice, o no se


debe forzar a los clientes a depender de métodos que no usan.

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() {
POO en Go 28

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

Los módulos de alto nivel no deben depender de módulos de bajo nivel. Ambos deberían
depender de abstracciones. Las abstracciones no deben depender de los detalles. Los
detalles deben depender de las abstracciones.

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:
POO en Go 29

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

Figura: Gopher - Mascota del Lenguaje Go ⁶

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:

• “Diseñar software orientado a objetos es difícil, y aún lo es más diseñar software orientado
a objetos reutilizable”.
• “Algo que los expertos saben que no hay que hacer es resolver cada problema partiendo de
cero. Por el contrario, reutilizan soluciones que ya les han sido útiles en el pasado. Cuando
encuentran una solución buena, la usan una y otra vez”.
• “Los patrones de diseño hacen que sea más fácil reutilizar buenos diseños y arquitecturas”.
• “Los patrones de diseño nos ayudan a elegir las alternativas de diseño que hacen que un
sistema sea reutilizable, y a evitar aquellas que dificultan dicha reutilización. Pueden incluso
mejorar la documentación y el mantenimiento de los sistemas existentes al proporcionar
una especificación explícita de las interacciones entre clases y objetos y de cuál es su
intención. En definitiva los patrones de diseño ayudan a un diseñador a lograr un buen
diseño más rápidamente”.

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
• Patrones de Diseño GoF
• Patrones de Comportamiento
• Patrones Creacionales
• Patrones Estructurales
⁶Fuente: https://golang.org/doc/gopher
GoF
¿Qué es Gof?
En esta publicación se utilizan 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)”

Es importante remarcar que este libro fue publicado en 1994 - (Java 1 recién se publicó
en 1996)

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
GoF 33

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. Intento no caer yo mismo en este error.

En Go pueden aprovecharse algunas de sus características particulares para implementar


también otros tipos de patrones distintos a los del libro Patrones de Diseño [38]. Por
ejemplo hay una gran cantidad de patrones de concurrencia.

¿Cómo se clasifican los patrones?


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

• Creacionales: tienen que ver con el proceso de creación de objetos.


• Estructurales: tratan con la composición de clases u objetos.
• Comportamiento: caracterizan el modo en el que las clases y objetos interactúan y se
reparten responsabilidades.

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. Estas 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”.

Al no existir en Go ni clases, ni objetos, ni herencia, la implementación de los patrones


se verán muy diferentes. El desafío de esta publicación es respetar el propósito de cada
patrón y su posible implementación en Go basándose a las características particulares
del lenguaje.

Catálogo de patrones
Los patrones de diseño se catalogan de la siguiente forma:
Según su Propósito:

• Creacionales: resuelven problemas relativos a la creación de objetos.


• Estructurales: resuelven problemas relativos a composición de objetos.
• Comportamiento: resuelven problemas relativos a la interacción de objetos.

Según su Ámbito:

• Clases: respecto de las relaciones estáticas entre clases.


• Objetos: respecto de las relaciones dinámicas entre objetos.
GoF 34

Patrones de Clase

Creación Estructurales Comportamiento


Factory Method Adapter Interpreter
Template Method

Extracto de Patrones de Diseño - Tabla 1.1 - Patrones de diseño - [38]

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

Extracto de Patrones de Diseño - Tabla 1.1 - Patrones de diseño - [38]

¿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.
GoF 35

Sección Detalle
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 se utiliza la misma estructura pero con las siguientes observaciones:

• Se reemplazan los escenarios por ejemplos más simples ya que los del libro están basados
en un caso de estudio complejo (creación de un editor de texto) y no pueden ser ejecutados
para el aprendizaje del lector. Los escenarios son triviales y muy simples, la idea es orientar
al lector en cómo se implementa el patrón de diseño y no en como resolver un problema
puntual. Se utilizan escenarios de alguno de los siguientes recursos de interés: [10], [11],
[12], [13].
• Se omiten las secciones aplicabilidad, colaboradores, consecuencias, usos conocidos y
patrones relacionados ya que no es el objetivo de esta publicación el explicar cada patrón de
diseño. Se insta al lector a referirse al libro Patrones de Diseño [38] para mayor información
- [38].
• Se unifica la motivación junto con el código de ejemplo.
• Se analiza adicionalmente, solo si corresponden, las alternativas de implementación desde
el punto de vista de la concurrencia que caracteriza al lenguaje Go.
• Se utiliza código de ejemplo en el lenguaje Go.
• Se utiliza UML en reemplazo de OMT.
GoF 36

Paradójicamente se implementan diagramas de clases de UML cuando en Go no existen


clases. Sin embargo, al ser UML una de las notaciones más extendidas, considero que la
traducción de las clases a tipos de datos de Go no presentará dificultades adicionales al
lector.

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 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

Figura: Gopher - Mascota del Lenguaje Go ⁷

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
• Chain of Responsibility
• Command
• Template Method
• Memento
• Interpreter
• Iterator
• Visitor
• State
• Mediator
• Observer

⁷Fuente: https://golang.org/doc/gopher
Patrones de Comportamiento 38

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
• Estrategia:
– declara la interfaz común a todos los algoritmos permitidos. El Contexto usa esta
interfaz para llamar al algoritmo definido por una EstrategiaConcreta.
• EstrategiaConcreta:
– implementa el algoritmo usando la interfaz Estrategia.
• Contexto:
– se configura con una variable EstrategiaConcreta.
– mantiene una referencia a una variable Estrategia.
– puede definir una interfaz que permita a la Estrategia acceder a sus datos.

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:
Patrones de Comportamiento 39

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


Patrones de Comportamiento 40

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


strategy
Patrones de Comportamiento 41

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
• Manejador:
– define una interfaz para tratar las peticiones.
– (opcional) implementa el enlace al sucesor.
• ManejadorConcreto:
– trata las peticiones de las que es responsable.
– puede acceder a su sucesor.
– si el ManejadorConcreto puede manejar la petición, lo hace; en caso contrario la
reenvía a su sucesor.
• Cliente:
– inicializa la petición a una variable ManejadorConcreto de la cadena.
Patrones de Comportamiento 42

Implementación
• No se observan impedimentos para su implementación en Go.
• La implementación de la clase abstracta Manejador debe reemplazarse por una interface
dado que no existe la herencia de clase en Go.
• Si fuese necesario que Manejador implementase código común a los ManejadoresConcretos
se podrá definir un tipo de dato adicional (ManejadorComun por ejemplo) y el mismo
deberá implementarse en cada ManejadorConcreto mediante el uso de la composició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 {
Patrones de Comportamiento 43

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
Patrones de Comportamiento 44

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
• Orden:
– declara una interfaz para ejecutar una operación.
• Orden Concreta:
– define un enlace entre una variable Receptor y una acción.
– implementa Ejecutar invocando la correspondiente operación u operaciones del Re-
ceptor.
• Cliente:
– crea una variable OrdenConcreta y establece su receptor.
• Invocador:
– le pide a la orden que ejecute la petición.
• Receptor:
– sabe cómo llevar a cabo las operaciones asociadas a una petición. Cualquier clase puede
actuar como Receptor.

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

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
Patrones de Comportamiento 46

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
Patrones de Comportamiento 47

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
• ClaseAbstracta:
– define operaciones primitivas abstractas que son definidas por los subtipos de datos
para implementar los pasos de un algoritmo.
– implementa una función plantilla que define el esqueleto de un algoritmo. La función
plantilla llama a las operaciones primitivas así como a operaciones definidas en
ClaseAbstracta o a las de otras variables.
• ClaseConcreta:
– implementa las operaciones primitivas para realizar los pasos del algoritmo específicos
de los subtipos de datos.

Implementación
• No se observan impedimentos para su implementación en Go.
Patrones de Comportamiento 48

• En este caso, dado que no existe la herencia de clase en Go, la ClaseAbstracta sugerida por
el patrón debe implementarse en dos partes: a) por un lado los comportamientos abstractos
deben definirse en una Interface, y b) por otro lado los comportamientos concretos (el
método plantilla) dentro del propio tipo ClaseAbstracta.
• Las ClasesConcretas se componen (en vez de heredar) de una ClaseConcreta.
• Las ClasesConcretas implementan los comportamientos de la Interface.
• La principal dificultad de implementar este patrón en Go es que el comportamiento
del método plantilla de la ClaseAbstracta invoca a otros comportamientos que no están
definidos dentro de la propia ClaseAbstracta sino dentro de la ClaseConcreta. Esto obliga
a que cuando se invoca el método plantilla desde una ClaseConcreta se deba pasar una
referencia de si misma para que el método plantilla pueda invocar los comportamientos
definidos en la Interface.

Dada esta complejidad adicional léase esta forma de implementación junto al código de
ejemplo del patró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 }
Patrones de Comportamiento 49

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.
Patrones de Comportamiento 50

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


Patrones de Comportamiento 51

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
• Memento:
– guarda el estado interno de la variable Creador. El memento puede guardar tanta
información del estado interno del creador como sea necesario a discreción del creador.
– protege frente a accesos de otras variables que no sean el creador. Los mementos
tienen realmente dos interfaces. El Conserje va una interfaz reducida del memento
- solo puede pasar el memento a otras variables -. El Creador, por el contrario, ve una
interfaz amplia, que le permite acceder a todos los datos necesarios para volver a su
estado anterior. Idealmente, solo el creador que produjo el memento estaría autorizado
a acceder al estado interno de este.
• Creador:
– crea un memento que contiene una instantánea de su estado interno actual.
– usa el memento para volver a su estado anterior.
• Conserje:
– es responsable de guardar en lugar seguro el memento.
– nunca examina los contenidos del memento, ni opera sobre ellos.

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

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:


Patrones de Comportamiento 53

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
Patrones de Comportamiento 54

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
• ExpresionAbstracta:
– declara una operación abstracta interpretar que es común a todos los nodos del árbol
de sintaxis abstracto.
• ExpresionTerminal:
– implementa una operación interpretar asociada con los símbolos terminales de la
gramática.
– se necesita una variable de este tipo de dato para cada símbolo terminal de la sentencia.
• ExpresionNoTerminal:
– por cada regla de la gramática debe haber uno de estos tipos de datos.
– mantiene variables de tipo ExpresionAbstracta para cada uno de los símbolos de cada
regla.
Patrones de Comportamiento 55

• Contexto:
– contiene información que es global al intérprete.
• Cliente:
– construye (o recibe) un árbol sintáctico abstracto que representa una determinada
sentencia del lenguaje definido por la gramática. Este árbol sintáctico abstracto está
formado por variables del tipo de dato ExpresionNoTerminal y ExpresionTerminal.
– invoca a la operación interpretar.

Implementación
• No se observan impedimentos y/o modificaciones de la estructura original del patrón para
su implementación en Go.
• La ExpresionAbstracta se define como una interfaz por simplificació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)
Patrones de Comportamiento 56

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
Patrones de Comportamiento 57

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
• Iterador:
– define una interfaz para recorrer los elementos y acceder a ellos.
• IteradorConcreto:
– implementa la interfaz Iterador.
– mantiene la posición actual en el recorrido del agregado.
• Agregado:
– define una interfaz para crear una variable Iterador.
• AgregadoConcreto:
– implementa la interfaz de creación de Iterador para devolver una variable del Iterador-
Concreto apropiado.
Patrones de Comportamiento 58

Implementación
• No se observan impedimentos y/o modificaciones de la estructura original del patrón para
su implementación en Go.
• El Iterador y Agregado se definen como interfaces por simplificació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++
Patrones de Comportamiento 59

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
Patrones de Comportamiento 60

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
• Visitante:
– declara una operación visitar para cada tipo de dato de operación de ElementoConcreto
de la estructura de variables. El nombre y signatura de la operación identifican al tipo
de dato que envía la petición visitar al visitante. Eso permite al visitante determinar el
tipo de dato concreto de elemento que está siendo visitada. A continuación el visitante
puede acceder al elemento directamente a través de su interfaz particular.
• VisitanteConcreto:
Patrones de Comportamiento 61

– implementa cada operación declarada por Visitante. Casa operación implementa un


fragmento del algoritmo definido para el tipo de dato correspondiente de la colección.
Visitanteconcreto proporciona el contexto para el algoritmo y guarda su estado local.
Muchas veces este estado acumula resultados durante el recorrido de la estructura.
• Elemento:
– define una operación aceptar que toma un visitante como argumento.
• ElementoConcreto:
– implementa una operación aceptar que toma un visitante como argumento.
• EstructuradDeObjeto:
– puede enumerar sus elementos.
– puede proporcionar una interfaz de alto nivel para permitir al visitante visitar a sus
elementos.
– puede ser un Composite o una colección, como una lista o un conjunto.

Implementación
• No se observan impedimentos y/o modificaciones de la estructura original del patrón para
su implementación en Go.
• Dado que Go no soporta sobrecarga de método, y a fin de facilitar el ejemplo sin hacer
uso de la reflexión (https://golang.org/pkg/reflect/), los visitantes dispondan de diferentes
comportamientos para cada tipo de elemento variando los nombres de sus funciones.
• Se omite del código de ejemplo EstructuraDeObjeto dado que solo sería una colección que
acepta elementos.

Código de ejemplo
En este ejemplo queremos recrear un juego de rol en donde algunos personajes tengan superpo-
deres 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 }
Patrones de Comportamiento 62

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{}
Patrones de Comportamiento 63

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
Patrones de Comportamiento 64

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
• Contexto:
– define la interfaz de interés para los clientes.
– mantiene una variable de un subtipo de dato de EstadoConcreto que define el estado
actual.
• Estado:
– define una interfaz para encapsular el comportamiento asociado con un determinado
estado del Contexto.
• Subtipos de Datos de EstadoConcreto:
– cada subtipo de dato implementa un comportamiento asociado con un estado del
Contexto.

Implementación
• No se observan impedimentos y/o modificaciones de la estructura original del patrón para
su implementación en Go.
• El Estado se define como interface por simplificación.
Patrones de Comportamiento 65

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:


Patrones de Comportamiento 66

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
Patrones de Comportamiento 67

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
• Mediador:
– define una interfaz para comunicarse con sus variables Colega.
• MediadorConcreto:
– implementa el comportamiento cooperativo coordinando variables Colega.
– conoce a sus Colegas.
• Colega:
– cada tipo de dato Colega conoce a su variable Mediador.
– cada Colega se comunica con su mediador cada vez que, de no existir éste, se hubiera
comunicado con otro Colega.

Implementación
• No se observan impedimentos y/o modificaciones de la estructura original del patrón para
su implementación en Go.
• El Mediador y Colega se definen como interfaces por simplificación.
Patrones de Comportamiento 68

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


Patrones de Comportamiento 69

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


mediator
Patrones de Comportamiento 70

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
• Sujeto:
– conoce a sus observadores. Un sujeto puede ser observado por cualquier número de
variables Observador.
– proporciona una interfaz para asignar y quitar variables Observador.
• Observador:
– define una interfaz para actualizar las variables que deben ser notificadas ante cambios
en un sujeto.
• SujetoConcreto:
Patrones de Comportamiento 71

– almacena el estado de interés para las variables ObservadorConcreto.


– envía una notificación a sus observadores cuando cambia su estado.
• ObservadorConcreto:
– mantiene una referencia a una variable SujetoConcreto.
– guarda un estado que debería ser consistente con el del sujeto.
– implementa la interfaz de actualización del Observador para mantener su estado
consistente.

Implementación
• No se observan impedimentos y/o modificaciones de la estructura original del patrón para
su implementación en Go.
• El Sujeto y Observador se definen como interfaces por simplificació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 {
Patrones de Comportamiento 72

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

Figura: Gopher - Mascota del Lenguaje Go ⁸

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
• Builder
• Factory Method
• Abstract Factory
• Prototype

⁸Fuente: https://golang.org/doc/gopher
Patrones Creacionales 74

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
• Singleton:
– define una operación instancia que permite que los clientes accedan a su única variable.
– puede ser responsable de crear su única variable en memoria.

Implementación
• No se observan impedimentos para la implementación del patrón en Go.
• Al no existir método y propiedades estáticas en Go es necesario utilizar programación
funcional para poder implementar el patrón.
• Dado que Go permite la programación concurrente en diferentes subprocesos de ejecución,
no es posible garantizar una única variable del tipo de dato si no se toman recaudos
adicionales. Para asegurar que solo existirá una variable del tipo de dato se deberá hacer uso
de la librería estándar sync (https://golang.org/pkg/sync/) de Go. Concretamente el método
Do (https://golang.org/pkg/sync/#Once.Do) de la estructura Once (https://golang.org/pkg/
sync/#Once) garantiza que la función pasada como parámetro puede ser ejecutada una
única vez mientras dure la ejecución del programa. Esta función será la encargada de crear
la única variable del tipo de dato Singleton.

Código de ejemplo
Implementación:
Patrones Creacionales 75

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
Patrones Creacionales 76

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
• Constructor:
– especifica una interfaz abstracta para crear las partes de una variable Producto.
• ConstructorConcreto:
– implementa la interfaz de Constructor para construir y ensamblar las partes del
producto.
– proporciona una interfaz para devolver el producto.
• Director:
– construye una variable usando la interfaz Constructor.
• Producto:
– representa una variable compleja en construcción. El ConstructorConcreto construye
la representación interna del producto y define el proceso de ensamble.
– incluye los tipos de datos que definen sus partes constituyentes, incluyendo interfaces
para ensamblar las partes en el resultado final.

Implementación
• No se observan impedimentos y/o modificaciones de la estructura original del patrón para
su implementación en Go.
• El Constructor se define como interface por simplificación.
Patrones Creacionales 77

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()
Patrones Creacionales 78

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
Patrones Creacionales 79

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
• Producto:
– define la interfaz de las variables que crea el método de fabricación.
• ProductoConcreto:
– implementa la interfaz Producto.
• Creador:
– declara el método de fabricación, el cual devuelve una variable de tipo Producto.
También puede definir una implementación predeterminada del método de fabricación
que devuelva una variable ProductoConcreto.
– puede llamar al método de fabricación para crear una variable Producto.
• CreadorConcreto:
– redefine el método de fabricación para devolver una variable de un ProductoConcreto.
Patrones Creacionales 80

Implementación
• No se observan impedimentos para su implementación en Go.
• En este caso, dado que no existe la herencia de clase en Go, el Creador sugerido por el patrón
debe implementarse en dos partes: a) por un lado los comportamientos abstractos deben
definirse en una Interface, y b) por otro lado los comportamientos concretos (el método una
operación) dentro del propio tipo de dato Creador.
• Los CreadoresConcretos se componen (en vez de heredar) de un Creador.
• Los CreadoresConcretos implementan los comportamientos de la InterfaceCreador.
• La principal dificultad de implementar este patrón en Go es que existe un comportamiento
concreto en Creador que invoca a otros comportamientos que no están definidos dentro del
propio Creador sino que están dentro del CreadorConcreto que implementa la Interface-
Creador. Esto obliga a que cuando se invoca el comportamiento concreto de Creador desde
un CreadorConcreto se deba pasar una referencia de si mismo para que el comportamiento
concreto pueda invocar los comportamientos definidos en la InterfaceCreador.

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
Patrones Creacionales 81

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
Patrones Creacionales 82

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
• FabricaAbstracta:
– declara una interfaz para operaciones que crean variables producto abstractas.
• FabricaConcreta:
– implementa las operaciones para crear variables producto concretos.
• ProductoAbstracto:
– declara una interfaz para un tipo de variable producto.
• ProductoConcreto:
– define una variable producto para que sea creado por la fábrica correspondiente.
– implementa la interfaz ProductoAbstracto.
• Cliente:
– solo usa interfaces declaradas por los tipos de datos FabricaAbstracta y ProductoAbs-
tracto.
Patrones Creacionales 83

Implementación
• No se observan impedimentos y/o modificaciones de la estructura original del patrón para
su implementación en Go.
• La FabricaAbstracta y ProductoAbstracto se definen como interfaces por simplificació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 }
Patrones Creacionales 84

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
Patrones Creacionales 85

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
• Prototipo:
– declara la interfaz para clonarse.
• PrototipoConcreto:
– implementa una operación para clonarse.
• Cliente:
– crea una variable pidiéndole a un prototipo que se clone.

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:
Patrones Creacionales 86

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

Figura: Gopher - Mascota del Lenguaje Go ⁹

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
• Adapter
• Bridge
• Proxy
• Decorator
• Facade
• Flyweight

⁹Fuente: https://golang.org/doc/gopher
Patrones Estructurales 88

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
• Componente:
– declara la interfaz de las variables de la composición.
– implementa el comportamiento predeterminado de la interfaz que es común a todos
los tipos de datos.
– declara una interfaz para acceder a sus componentes hijos y gestionarlos.
– (opcional) define una interfaz para acceder al padre de un componente en la jerarquía
recursiva y, si es necesario, la implementa.
• Hoja:
– representa variables hoja en la composición. Una hoja no tiene hijos.
– define el comportamiento de las variables primitivas de la composición.
• Compuesto:
– define el comportamiento de los componentes que tienen hijos.
– almacena componentes hijos.
– implementa las operaciones de la interfaz Componente relacionadas con los hijos.
• Cliente:
– manipula variables en la composición a través de la interfaz Componente.
Patrones Estructurales 89

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 }
Patrones Estructurales 90

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
Patrones Estructurales 91

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
• Objetivo:
– define la interfaz específica del dominio que usa el Cliente.
• Cliente:
– colabora con variables que se ajustan a la interfaz Objetivo.
• Adaptable:
– define una interfaz existente que necesita ser adaptada.
• Adaptador:
– adapta la interfaz de Adaptable a la interfaz Objetivo.

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

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:


Patrones Estructurales 93

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
Patrones Estructurales 94

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
• Abstraccion:
– define la interfaz de la abstracción.
– mantiene una referencia a una variable de tipo Implementador.
• AbstraccionRefinada:
– extiende la interfaz definida por Abstraccion.
• Implementador:
– define la interfaz de los tipos de datos de implementación. Esta interfaz no tiene por
qué corresponderse exactamente con la de Abstracción; de hecho, ambas interfaces
pueden ser muy distintas. Normalmente la interfaz Implementador solo proporciona
operaciones primitivas, y Abstraccion define operaciones de más alto nivel basadas en
dichas primitivas.
• ImplementadorConcreto:
– implementa la interfaz Implementador y define su implementación concreta.
Patrones Estructurales 95

Implementación
• No se observan impedimentos para su implementación en Go.
• En este caso, dado que Abstraccion se define como una interface pero a la vez también
implementando comportamiento concreto para mantener la referencia del tipo de dato
Implementador, se separará en dos partes en dos partes: a) por un lado los comportamientos
abstractos deben definirse en una interface Abstraccion Interface, y b) por otro lado los
comportamientos concretos (el que mantiene una referencia del Implementador) dentro de
un tipo de dato Abstraccion Abstracta.
• Las Abstracciones Refinadas se componen (en vez de heredar) de Abstraccion Abstracta.

Código de ejemplo
En este ejemplo queremos desacoplar el protocolo de conexión a internet que pueden implemen-
tar 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
Patrones Estructurales 96

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
Patrones Estructurales 97

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
• Proxy:
– mantiene una referencia que permite al proxy acceder a la variable original. El proxy
puede referirse a un Sujeto en caso de que las interfaces de SujetoReal y Sujeto sean la
misma.
– proporciona una interfaz idéntica a la de Sujeto, de manera que un proxy pueda ser
sustituido por el sujeto real.
– controla el acceso al sujeto real, y puede ser responsable de su creación y borrado.
– otras responsabilidades dependen del tipo de proxy:
* los proxies remotos son responsables de codificar una petición y sus argumentos
para enviar la petición codificada al sujeto real que se encuentra en un espacio de
direcciones diferente.
* los proxies virtuales pueden guardar información adicional sobre el sujeto real, por
lo que pueden retardar el acceso al mismo.
Patrones Estructurales 98

* los proxies de protección comprueban que el llamador tenga los permisos de acceso
necesarios para realizar una petición.
• Sujeto:
– define una interfaz común para el SujetoReal y el Proxy, de modo que pueda usarse un
Proxy en cualquier sitio en el que se espere un SujetoReal.
• SujetoReal:
– define la variable real representada.

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:


Patrones Estructurales 99

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
Patrones Estructurales 100

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
• Componente:
– define la interfaz para variables a las que se puede añadir responsabilidades dinámica-
mente.
• ComponenteConcreto:
– define una variable a la que se pueden añadir responsabilidades adicionales.
• Decorador:
– mantiene una referencia a una variable Componente y define una interfaz que se ajusta
a la interfaz del Componente.
• DecoradorConcreto:
– añade responsabilidades al componente.
Patrones Estructurales 101

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 }
Patrones Estructurales 102

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
Patrones Estructurales 103

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
• Fachada:
– sabe qué tipos de datos del subsistema son los responsables ante una petición.
– delega las peticiones de los clientes en las variables apropiadas del subsistema.
• Tipos del subsistema:
– implementa la funcionalidad del subsistema.
– realizan las labores encomendadas por la variable Fachada.
– no conocen a la fachada; es decir, no tienen referencias a ella.

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:
Patrones Estructurales 104

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 }
Patrones Estructurales 105

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
Patrones Estructurales 106

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
• Flyweight:
– declara una interfaz a través de la cual los flyweight concretos pueden recibir un estado
extrínseco y actuar sobre él.
• FlyweightConcreto:
– implementa la interfaz Flyweight y permite almacenar el estado intrínseco, en caso de
que lo haya. Una variable FlyweightConcreto debe poder ser compartida, por lo que
cualquier estado que almacene debe ser intrínseco, esto es, debe ser independiente del
contexto de la variable FlyweightConcreto.
• FlyweightConcretoNoCompartido:
– no todas los subtipos de datos de Flyweight necesitan ser compartidas. La interfaz
Flyweight permite el comportamiento, no fuerza a él. Las variables FlyweightConcre-
toNoCompartido suelen tener variables FlyweightConcreto como hijos en algún nivel
de la jerarquía de variables.
Patrones Estructurales 107

• FabricaFlyweight:
– crea y controla variables flyweight.
– garantiza que los flyweight se compartan de manera adecuada. Cuando un cliente
solicita un flyweight, la variable FabricaFlyweight proporciona una variable concreta
o crea una nueva, en caso de que no exista ninguna.
• Cliente:
– mantiene una referencia a los flyweight.
– calcula o guarda el estado extrínseco de los flyweight.

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
Patrones Estructurales 108

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:

No deberían programar en Go como lo hacen en Java, PHP, C#, etc. Deberían potenciar
las características propias del lenguaje en sus desarrollos; esto es comprender a fondo,
por ejemplo, como funcionan las interfaces, la composición y la concurrencia; y sobre esta
base reutilizar conceptos o usos de patrones de diseño cuando realmente se justifiquen.
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].

Figura: Logo de Pontificia Universidad Católica Argentina ¹⁰

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”
¹⁰Fuente: http://uca.edu.ar
Acerca De 112

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 si-


guiente 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.
Acerca De 113

Figura: Evolución de la popularidad de Go ¹¹

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

Figura: Ranking de popularidad de Go ¹²


¹¹Fuente: https://www.tiobe.com/tiobe-index/go/
¹²Fuente: https://www.tiobe.com/tiobe-index
Acerca De 114

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ª.

Las estadísticas registradas de Tiobe corresponden a Mayo del 2018. Consulte el Índice
Tiobe [51] para tener datos más actualizados.

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:

• hacer un muestreo de cuatro o cinco patrones GoF aleatoriamente con el fin de analizar si
se podían implementar “semánticamente” en Go.
• analizar si esas implementaciones realmente podían aportar valor al software.

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 suficien-
temente 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();
Acerca De 115

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:

• no tiene forma de validar que lo que se esté pasando al Transportista sea una Estrategia
válida.
• no tiene forma de implementar una interface Estrategia o heredar de una clase Estrategia.
• no permite generar código reutilizable en paquetes o namespaces.
• el Contexto no es autodocumentable a nivel código fuente ya que su función setStrategy
espera cualquier tipo de variable y no un objeto de tipo Estrategia.

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.

Aclaro ES5 de Javascript porque ES6 tiene un soporte más avanzado para la programa-
ción orientada a objetos.

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:

• documentar los 23 patrones de diseño GoF en Go


Acerca De 116

• analizar como se pueden implementar prácticas y conceptos propios de la programación


orientada a objetos en Go.
• y finalmente, redactar esta publicación.

Las herramientas utilizadas


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

• que fueran gratuitas y/o de libre acceso


• que fueran open source

Las herramientas utilizadas fueron:

• GitBook: plataforma para la primera publicación de este trabajo como e-book. - https://
legacy.gitbook.com/
• Leanpub: plataforma para la publicación actual de este trabajo como e-book. - https:
//leanpub.com/
• GitHub: plataforma para la publicación del código fuente de esta publicación. - https://
github.com/
• Git: software para el control de versiones del código fuente de esta publicación. - https:
//git-scm.com/
• Markdown: lenguaje de marcado para la redacción de la primera publicación. - https://
daringfireball.net/projects/markdown/
• Markua: lenguaje de marcado para la redacción de la publicación actual. - http://markua.
com/
• Go Playground: plataforma para la publicación y ejecución de códigos de ejemplo de Go.
- https://play.golang.org/
• Violet UML Editor: software para la generación de diagramas UML. - http://alexdp.free.fr/
violetumleditor

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

• Para la generación de la publicación se analizaron otras alternativas a Leanpub como: a)


hacer una web puramente en HTML/CSS, b) hacer una web con algún CMS como Joomla
o WordPress, y c) utilizar un generador de ebook como Pandoc o similares. Sin embargo
la primera elección fue GitBook ya que integraba varias características como la auto
publicación online, la redacción en markdown, la exportación a otros formatos (ePub, PDF,
Mobi), y un entorno de colaboración con terceros mediante la publicación de comentarios y
notas. A Mayo de 2020 el e-book fue transferido a Leanpub dado que GitBook se actualizo a
una nueva versión con un modelo de negocio más orientado a la documentación online de
equipos y su plan free paso a carecer de las principales características por las que había sido
elegido en un primer momento. Leanpub es la elección actual ya que posee en excelente
soporte para la publicación de e-books.
Acerca De 117

• Para la publicación de códigos fuentes se analizaron otras plataformas a GitHub como:


a) GitLab, o b) Bitbucket. Sin embargo la elección fue GitHub ya que, de las tres, en mi
experiencia es la que más proyectos open source aloja. Siendo mi deseo que esta publicación
pueda ser el punto de partida para otros fork y discusiones en la comunidad de Go.
• Para el control de versiones se analizó también SVN. Sin embargo la elección fue Git ya
que a diferencia de SVN no requiere de un repositorio central facilitando el trabajo de la
redacción de la publicación en diferentes equipos y lugares.
• Para el lenguaje de redacción de la publicación se analizaron otras alternativas a Markdown
como: a) HTML, b) OpenDocument, y c) reStructuredText. Sin embargo la primera elección
fue Markdown ya que resulta ser un lenguaje mucho más simple de aprender que los
anteriores y existe una gran cantidad de herramientas para convertir el mismo en otros
formatos. Este punto es muy importante para facilitar una posterior colaboración y
ampliación de la publicación mediante la comunidad interesada en Go. A mayo de 2020
junto con la migración de GitBook a Leanpub se actualizó de Markdown a Markua siendo
este último una extensión del primero orientada a la redacción exclusiva de e-books.
• Para la generación de diagramas UML se analizaron otras alternativas a Violet UML como:
a) StarUML, b) Diagram Designer, y c) ModelioSoft. Sin embargo la elección fue Violet
UML ya que se encuentra disponible para varios Sistemas Operativos y es muy simple
de aprender dado que no tiene demasiadas opciones y tipos de diagramas disponibles. La
única restricción importante que presento este software es la falta de estilos que imposibilita
estereotipar con cursiva a aquellas clases que son abstractas.
• Para la ejecución online de código de ejemplo en Go no analice otras alternativas ya que
Go Playgroung es la herramienta oficial de Go y se encuentra muy popularizada en su
comunidad.

Agradecimientos
Quisiera concluir agradeciendo a:

• mis compañeros de cursada por los buenos momentos vividos y las experiencias comparti-
das.
• a la UCA por la excelente infraestructura y organización de la Carrera.
• al cuerpo docente por su alto nivel académico y la predisposición para transmitir sus
conocimientos.
• a mi tutor de trabajo final por su compromiso, sus consejos y acompañamiento durante la
realización de este trabajo.
• a mis seres queridos por apoyarme en este viaje a pesar del tiempo que no les pude dedicar.
Recursos de Interés
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
Fuente: https://golang.org
[2] - Documentación oficial
Fuente: https://golang.org/doc
[3] - Preguntas frecuentes oficiales
Fuente: https://golang.org/doc/faq
[4] - Consola interactiva
Fuente: https://play.golang.org
[5] - Blog oficial
Fuente: https://blog.golang.org

Artículos Web
[6] - Let’s Go: Object-Oriented Programming in Golang
Fuente: https://code.tutsplus.com/tutorials/lets-go-object-oriented-programming-in-golang--cms-
26540
[7] - SOLID Go Design
Fuente: https://dave.cheney.net/2016/08/20/solid-go-design
[8] - Is Go An Object Oriented Language?
Fuente: http://spf13.com/post/is-go-object-oriented
[9] - Object Orientation In Go
Fuente: https://katcipis.github.io/blog/object-orientation-go
[10] - Design Patterns for Humans!
Fuente: https://github.com/kamranahmedse/design-patterns-for-humans
[11] - Java Design Patterns - Example Tutorial
Fuente: https://www.journaldev.com/1827/java-design-patterns-example-tutorial
[12] - Design patterns implemented in Java
Fuente: http://java-design-patterns.com
[13] - Design Patterns in Java Tutorial
Fuente: https://www.tutorialspoint.com/design_pattern
Recursos de Interés 119

[14] - Why extends is evil


Fuente: https://www.javaworld.com/article/2073649/core-java/why-extends-is-evil.html
[15] - Inheritance vs Composition
Fuente: https://www.techjini.com/blog/inheritance-vs-composition
[16] - Go is not good
Fuente: https://github.com/ksimka/go-is-not-good
[17] - Go at Google: Language Design in the Service of Software Engineering
Fuente: https://talks.golang.org/2012/splash.article

Papers
[18] - Publicaciones académicas
Fuente: https://github.com/golang/go/wiki/ResearchPapers
[19] - GoHotDraw: Evaluating the Go Programming Language with Design Patterns - Frank
Schmager, Nicholas Cameron, James Noble
Fuente: http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.188.5524&rep=rep1&type=pdf

Libros
[20] - Karl Seguin, “The Litte Go Book”, 2014
Fuente: https://github.com/karlseguin/the-little-go-book
[21] - Hemant Jain, “Data Structures & Algorithms In Go” - WOW, 2017
Fuente: https://www.amazon.com/Data-Structures-Algorithms-Hemant-Jain-ebook/dp/B075TBM9KS
[22] - Matt Aimoneaatti, “Go Bootcamp” - 2014
Fuente: http://www.golangbootcamp.com
[23] - Aaron Torres, “Go Cookbook” - Packt, 2017
Fuente: https://www.packtpub.com/application-development/go-cookbook
[24] - Mario Castro Contreras, “Go Design Patterns” - Packt, 2017
Fuente: https://www.packtpub.com/application-development/go-design-patterns
[25] - Vladimir Vivien, Mario Castro Contreras, Mat Ryer, “Go: Design Patterns for Real-World
Projects” - Packt, 2017
Fuente: https://www.packtpub.com/application-development/go-design-patterns-real-world-projects
[26] - Matt Butcher, Matt Farina, “Go In Practice” - Manning, 2016
Fuente: https://www.manning.com/books/go-in-practice
[27] - Mat Ryer, “Go Programming Blueprints Second Edition” - Packt, 2016
Fuente: https://www.packtpub.com/application-development/go-programming-blueprints-second-
edition
[28] - Shiju Varghese, “Go Recipes” - Apress, 2016
Fuente: https://www.apress.com/br/book/9781484211892
[29] - Mihalis Tsoukalos, “Go Systems Programming” - Packt, 2017
Fuente: https://www.packtpub.com/networking-and-servers/go-systems-programming
Recursos de Interés 120

[30] - Sau Sheong Chang, “Go Web Programming” - Manning, 2016


Fuente: https://www.manning.com/books/go-web-programming
[31] - Vladimir Vivien, “Learning Go Programming” - Packt, 2016
Fuente: https://www.packtpub.com/application-development/learning-go-programming
[32] - Nathan Zozyra, “Learning Go Web Development” - Packt, 2016
Fuente: https://www.packtpub.com/web-development/learning-go-web-development
[33] - Daniel Whitenack, “Machine Learning With Go” - Packt, 2017
Fuente: https://www.packtpub.com/big-data-and-business-intelligence/machine-learning-go
[34] - Jan Newmarch, “Network Programming with Go” - Apress, 2017
Fuente: https://www.apress.com/de/book/9781484226919
[35] - Nathan Zozyra, Mat Ryer, “Go: Building Web Applications” - Packt, 2016
Fuente: https://www.packtpub.com/application-development/go-building-web-applications
[36] - Mark Summerfield, “Programming in Go” - Addison-Wesley Professional, 2012
Fuente: https://www.amazon.com/Programming-Go-Creating-Applications-Developers/dp/0321774639
[37] - Thorsten Ball, “Writing an Interpreter in Go” - 2016
Fuente: https://interpreterbook.com
[38] - Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides, “Patrones de Diseño” -
Addison Wesley, 2002
Fuente: https://www.amazon.es/Patrones-dise%C3%B1o-Erich-Gamma/dp/8478290591
[39] - Junade Ali, “Mastering PHP Design Patterns” - Packt, 2016
Fuente: https://www.packtpub.com/application-development/mastering-php-design-patterns
[40] - Robert C. Martin, “Agile Software Development, Principles, Patterns, and Practices” - Alan
Apt Series, 2002
Fuente: https://www.amazon.es/Software-Development-Principles-Patterns-Practices/dp/0135974445
[41] - Caleb Doxsey, “Introducing Go” - O’Reilly, 2016
Fuente: http://shop.oreilly.com/product/0636920046516.do

Presentaciones
[42] - Go for Javaneros
Fuente: https://talks.golang.org/2014/go4java.slide#1
[43] - Rob Pike - Simplicity is Complicated
Fuente: https://www.youtube.com/watch?v=rFejpH_tAHM
[44] - Rob Pike - Go Proverbs
Fuente: https://www.youtube.com/watch?v=PAAkCSZUG1c
[45] - Francesc Campoy - Google Understanding Go Interfaces
Fuente: https://www.youtube.com/watch?v=F4wUrj6pmSI
[46] - Dave Cheney - SOLID Go Design
Fuente: https://www.youtube.com/watch?v=zzAdEt3xZ1M
[47] - William Kennedy - Golang OOP - Composition in Go
Fuente: https://www.youtube.com/watch?v=194blNHDdd0
Recursos de Interés 121

Otros Recursos
[48] - Librerías, paquetes y framewoks de Go
Fuente: https://github.com/avelino/awesome-go
[49] - Gophers oficiales
Fuente: https://golang.org/doc/gopher
[50] - Gophers libres
Fuente: https://github.com/golang-samples/gopher-vector
[51] - Índice Tiobe
Fuente: https://www.tiobe.com/tiobe-index
[52] - Logos oficiales de Go
Fuente: https://golang.org/s/logos
[53] - UCA - Pontificia Universidad Católica Argentina
Fuente: http://uca.edu.ar
[54] - Python - Glosario
Fuente: https://docs.python.org/3/glossary.html?highlight=duck#term-duck-typing
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.

También podría gustarte