En esta página:
En Go las interfaces permiten a los Desarrolladores tratar diferentes Types como el mismo Type que se usa para comportamientos específicos. Los Types son fundamentales para funcionar con el conjunto de herramientas para Desarrolladores en Go y a menudo los nuevos Desarrolladores de Go los usan de manera incorrecta, que hace que se generen códigos difíciles de leer y con errores. En este Post te enseñare Buenas Prácticas para crear interfaces limpias en Go.
Antes de continuar, te invito a leer el artículo: Que es Go y otros Detalles:
Para que estés familiarizado con este Post y también te invito a escuchar el Podcast: “Donde buscar ayuda sobre Programación”:
Spotify:
Sound Cloud:
Bien ahora continuemos con el Post: Buenas Prácticas para Crear Interfaces Limpias en Go
Las interfaces de Go se pueden denominar como colecciones de firmas de métodos que permiten lograr un tipo de poliformismo en Go. Veamos a continuación algunas Buenas prácticas para crear Interfaces limpias en Go.
Resumen de Interfaces
La librería estándar de Go es un ejemplo de como escribir de manera limpia en Go, por ejemplo la interface de error es simple:
1 2 3 4 5 |
type error interface { Error() string } |
Por definición, la interfaz de error encapsula cualquier tipo que tenga un método Error () definido, no acepta parámetros y devuelve una cadena. Por ejemplo, definamos una estructura que represente una problema de red:
1 2 3 4 5 6 |
type problemadeRed struct { mensaje string codigo int } |
Paso seguido definamos un método Error ():
1 2 3 4 5 |
func (pr problemadeRed) Error() string { return fmt.Sprintf("Error de red! mensaje: %s, codigo: %v", pr.mensaje, pr.codigo) } |
Ahora podemos usar una instancia de la estructurar problemadeRed siempre que se acepte un error:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
func handleErr(err error) { fmt.Println(err.Error()) } pr := problemadeRed{ mensaje: "Recibimos un problema", codigo: 404, } handleErr(pr) // prints "Error de red! mensaje: Recibimos un problema, codigo: 404" |
Mantén las interfaces pequeñas
Un buen consejo que te podría dar es mantener las interfaces pequeñas. Las interfaces están destinadas a definir el comportamiento mínimo necesario para representar con precisión alguna entidad.
A continuación un ejemplo de una interfaz más grande que sigue siendo un buen ejemplo de definición de comportamiento mínimo:
1 2 3 4 5 6 7 8 9 |
type File interface { io.Closer io.Reader io.Seeker Readdir(count int) ([]os.FileInfo, error) Stat() (os.FileInfo, error) } |
El paquete HTTP en Go puede considerar cualquier Type que satisfaga los comportamientos de la interface como un archivo. Eso es conveniente porque el paquete HTTP no necesita saber si se trata de un archivo en el disco, un búfer de red o un byte simple [].
Las interfaces no deben conocer los Types satisfactorios
Una interfaz debe definir qué es necesario para que otros Types se clasifiquen como miembros de esa interfaz. No deben estar al tanto de ningún Type que satisfaga la interfaz en el momento de su creación.
Por ejemplo, supongamos que estamos construyendo una interfaz para describir los componentes necesarios para definir un auto.
1 2 3 4 5 6 7 |
type auto interface { ObtenerColor() string ObtenerVelocidad() int EsDeportivo() bool } |
Los métodos ObtenerColor() y ObtenerVelocidad() están limitados al alcance de un auto y EsDeportivo() es un antipatrón. En el código supongamos estamos obligando a todos los autos autos a declarar si son o no Deportivos. Para que este patrón tenga sentido, necesitaríamos una lista completa de posibles subtipos como EsConvertible(), EsSedan(), etc.
En cambio el Desarrollador debería haber confiado en la funcionalidad nativa de la aserción de Type para derivar el tipo subyacente cuando se le dio una instancia de la interfaz auto() o si se necesita una subinterfaz, se puede definir como:
1 2 3 4 5 6 |
type deportivo interface { auto Kilometraje() int } |
La anterior subinterfaz hereda los métodos necesarios del auto y agrega un método adicional requerido Kilometraje().
Las Interfaces no son Clases
Las interfaces tienen determinadas características:
- Las interfaces no tienen constructores o deconstructores que requieren que los datos se creen o se destruyan.
- Las interfaces no son jerárquicas por naturaleza, aunque existen formas para crear interfaces que resultan ser superconjuntos de otras interfaces.
- Las interfaces definen las firmas de funciones, pero no el comportamiento subyacente. Esto significa que hacer una interfaz a menudo no afectará con respecto a los métodos de estructura. Si cinco Types satisfacen la interfaz de error, todos necesitan su propia versión de la función Error().
Conclusión
Existen otros conceptos y técnicas para crear interfaces limpias en Go, en este Post te he compartido algunas que yo por experiencia conozco y si hay otras buenas prácticas que crees deberíamos considerar, por favor escribelas en los comentarios.
Nota(s)
- Los ejemplos de códigos presentados en este Post pueden cambiar, ser modificados o continuar en futuras versiones de Go, esto no depende de mi, si no de los Desarrolladores que dan soporte a Go.
- No olvides que debemos usar la Tecnología para hacer cosas Buenas por el Mundo.
Síguenos en nuestras Redes Sociales para que no te pierdas nuestros próximos contenidos.