Programación orientada a objetos en Go
Jan 05, 2023
Updated: Jun 24, 2026

Programación orientada a objetos en Go

La programación orientada a objetos (POO) es un paradigma de programación que se centra en la abstracción de los componentes de un programa para crear una relación entre ellos. Esto se logra usando objetos, y estos objetos pueden interactuar entre sí mediante mensajes. Estos objetos tienen atributos (propiedades) y comportamientos (métodos).

La filosofía del diseño de Go

Las raíces de Go están basadas en C y, más ampliamente, en la familia Algol. Ken Thompson dijo medio en broma que Rob Pike, Robert Griesemer y él mismo se reunieron y decidieron que odiaban C++. Sea una broma o no, Go es muy diferente de C++. Más detalles sobre eso adelante. Go se trata de la simplicidad máxima. Esto lo explica de manera detallada Rob Pike en "Menos es exponencialmente más".

Go frente a otros lenguajes

Go no tiene clases, objetos, excepciones ni plantillas. Tiene recolección de basura y concurrencia integrada. La omisión más notable en lo que respecta a la orientación a objetos es que en Go no hay una jerarquía de tipos. Esto contrasta con la mayoría de los lenguajes orientados a objetos como C++, Java, C#, Scala, e incluso con los lenguajes dinámicos como Python y Ruby.

Características del lenguaje orientado a objetos de Go

Go no tiene clases, pero tiene tipos. En particular, tiene estructuras (structs). Las estructuras son tipos definidos por el usuario. Los tipos de estructura (con métodos) sirven para fines similares a las clases en otros lenguajes.

Estructuras (structs)

Una estructura define el estado. Más adelante veremos una estructura Creature. Tiene un campo de nombre (Name) y una bandera booleana llamada Real, que nos dice si es una criatura real o imaginaria. Las estructuras únicamente mantienen el estado y no el comportamiento.

En Go, la programación orientada a objetos se logra mediante el uso de structs para definir el estado de los objetos, y el uso de métodos para manipular ese estado. Los métodos también sirven para definir comportamientos. Los métodos se definen como funciones, pero reciben un valor receiver que les permite acceder al estado de los objetos. Esto es útil para organizar el código y mejorar la reutilización.

Veamos un primer ejemplo. Aquí un Dog incrusta de forma anónima a un Animal, así que hereda sus campos y sus métodos por composición:

package main

import "fmt"

type Animal struct {
	Name string
	Age  int
}

func (a Animal) Speak() {
	fmt.Println("Hola!")
}

func (a Animal) Walk() {
	fmt.Println("Caminando...")
}

type Dog struct {
	Animal
	Breed string
	Size  int
}

func (d Dog) Bark() {
	fmt.Println("¡Guau!")
}

func (d Dog) Bite() {
	fmt.Println("Mordiendo...")
}

func main() {
	d := Dog{
		Animal: Animal{
			Name: "Fido",
			Age:  5,
		},
		Breed: "Dálmata",
		Size:  10,
	}
	d.Speak()
	d.Walk()
	d.Bark()
	d.Bite()
}

Incrustación anónima

En Go puedes incrustar tipos unos dentro de otros. Por ejemplo, si incrustas una estructura sin nombrar el campo, la estructura incrustada aporta su estado (y sus métodos) directamente a la estructura que la contiene. Veamos un ejemplo con FlyingCreature, que incrusta a Creature:

type Creature struct {
	Name string
	Real bool
}

type FlyingCreature struct {
	Creature
	WingSpan int
}

dragon := &FlyingCreature{
	Creature{"Dragon", false},
	15,
}

fmt.Println(dragon.Name)
fmt.Println(dragon.Real)
fmt.Println(dragon.WingSpan)

Fíjate en que accedemos a dragon.Name y dragon.Real directamente, aunque esos campos pertenecen a Creature. Eso es lo que nos regala la incrustación anónima.

Interfaces en Go

Las interfaces son el sello distintivo del soporte orientado a objetos de Go. Son tipos que declaran conjuntos de métodos, pero no tienen implementación. Las interfaces definen el comportamiento de un objeto, y cualquier objeto que implemente una interfaz debe implementar esos comportamientos. Lo interesante es que en Go la implementación es implícita: no declaras que un tipo implementa una interfaz, simplemente basta con que tenga los métodos. Por ejemplo, podemos definir una interfaz Animal:

type Animal interface {
	MakeNoise() string
	Move() string
}

type Dog struct {
	breed string
	age   int
}

func (d Dog) MakeNoise() string {
	return "Woof!"
}

func (d Dog) Move() string {
	return "Run!"
}

Como Dog tiene los métodos MakeNoise() y Move(), satisface la interfaz Animal automáticamente.

Encapsulamiento en Go

Go encapsula las cosas a nivel de paquete. Los nombres que empiezan con una letra minúscula solo son visibles dentro de ese paquete. Puedes ocultar cualquier cosa en un paquete y exponer únicamente los tipos, interfaces y funciones de fábrica que quieras.

Herencia en Go

La herencia siempre ha sido un tema controvertido en la programación orientada a objetos. Go no tiene ningún tipo de jerarquía, pero permite compartir detalles de implementación a través de la composición. La composición mediante la incrustación de un tipo anónimo es equivalente a la herencia de la implementación.

Polimorfismo en Go

El polimorfismo es la esencia de la programación orientada a objetos: la capacidad de tratar objetos de diferentes tipos de forma uniforme siempre que se adhieran a la misma interfaz. Las interfaces de Go brindan esta capacidad de una manera muy directa e intuitiva.

En resumen, Go es un lenguaje de programación potente y flexible que ofrece una amplia gama de características para la programación orientada a objetos. Con su enfoque en la composición, las interfaces y la incrustación anónima, Go proporciona una experiencia única y efectiva para quienes desean aprovechar al máximo este paradigma. Diviértete explorando Go.

Diseño orientado a objetos: la forma de Go

En esta sección profundizaremos en cómo se compara Go con los pilares de la programación orientada a objetos: encapsulación, herencia y polimorfismo.

Encapsulación en Go (revisión)

Ya discutimos cómo Go encapsula cosas a nivel de paquete, pero profundicemos con un ejemplo más. Para ocultar el tipo foo y exponer únicamente la interfaz, lo declaramos en minúsculas y proporcionamos una función NewFoo() que devuelve la interfaz pública Fooer:

type Fooer interface {
	Foo1()
	Foo2()
	Foo3()
}

type foo struct {
}

func (f foo) Foo1() {
	fmt.Println("Foo1() here")
}

func (f foo) Foo2() {
	fmt.Println("Foo2() here")
}

func (f foo) Foo3() {
	fmt.Println("Foo3() here")
}

func NewFoo() Fooer {
	return &foo{}
}

Como foo está en minúsculas, queda oculto fuera del paquete. Quien consuma tu paquete solo verá la interfaz Fooer y la función NewFoo().

Herencia en Go (revisión)

Como mencionamos antes, Go no tiene ningún tipo de jerarquía, pero permite compartir detalles de implementación a través de la composición. La composición mediante la incrustación de un tipo anónimo es equivalente a la herencia de la implementación.

Aquí hay un ejemplo en el que SuperFooer incrusta la interfaz Fooer, pero no implementa sus métodos. El compilador de Go te permitirá crear un nuevo SuperFooer y llamar a los métodos de Fooer, pero obviamente fallará en tiempo de ejecución, porque la interfaz incrustada es nil:

type SuperFooer struct {
	Fooer
}

func main() {
	s := SuperFooer{}
	s.Foo2()
}

Polimorfismo en Go (revisión)

El polimorfismo en Go se logra mediante interfaces. Aquí tienes un ejemplo en el que se crean múltiples criaturas (¡y una puerta!) que implementan la interfaz Dumper y luego se llama al método Dump() para cada una:

package main

import "fmt"

type Creature struct {
	Name string
	Real bool
}

func Dump(c *Creature) {
	fmt.Printf("Name: '%s', Real: %t\n", c.Name, c.Real)
}

func (c Creature) Dump() {
	fmt.Printf("Name: '%s', Real: %t\n", c.Name, c.Real)
}

type FlyingCreature struct {
	Creature
	WingSpan int
}

func (fc FlyingCreature) Dump() {
	fmt.Printf("Name: '%s', Real: %t, WingSpan: %d\n",
		fc.Name,
		fc.Real,
		fc.WingSpan)
}

type Unicorn struct {
	Creature
}

type Dragon struct {
	FlyingCreature
}

type Pterodactyl struct {
	FlyingCreature
}

func NewPterodactyl(wingSpan int) *Pterodactyl {
	pet := &Pterodactyl{
		FlyingCreature{
			Creature{"Pterodactyl", true},
			wingSpan,
		},
	}
	return pet
}

type Dumper interface {
	Dump()
}

type Door struct {
	Thickness int
	Color     string
}

func (d Door) Dump() {
	fmt.Printf("Door => Thickness: %d, Color: %s", d.Thickness, d.Color)
}

func main() {
	creature := &Creature{"some creature", false}
	uni := Unicorn{
		Creature{"Unicorn", false},
	}
	pet1 := &Pterodactyl{
		FlyingCreature{
			Creature{"Pterodactyl", true},
			5,
		},
	}
	pet2 := NewPterodactyl(8)
	door := &Door{3, "red"}

	Dump(creature)
	creature.Dump()
	uni.Dump()
	pet1.Dump()
	pet2.Dump()

	creatures := []Creature{
		*creature,
		uni.Creature,
		pet1.Creature,
		pet2.Creature,
	}
	fmt.Println("Dump() through Creature embedded type")
	for _, creature := range creatures {
		creature.Dump()
	}

	dumpers := []Dumper{creature, uni, pet1, pet2, door}
	fmt.Println("Dump() through Dumper interface")
	for _, dumper := range dumpers {
		dumper.Dump()
	}
}

Conclusión

Go es un verdadero lenguaje de programación orientado a objetos. Permite el modelado basado en objetos y promueve la buena práctica de utilizar interfaces en vez de jerarquías de tipos concretos. Go tomó algunas decisiones sintácticas atípicas, pero en general, trabajar con tipos, métodos e interfaces se siente simple, ligero y natural.

La herencia es uno de los principales conceptos relacionados con la programación orientada a objetos, y Go la soporta a través de la sintaxis de la composición. Esto significa que los tipos de Go pueden tener uno o más campos asociados a otros tipos. Al mismo tiempo, los métodos asociados a un tipo pueden llamar a los métodos de los tipos asociados. Esto permite una forma de herencia más simple, sin necesidad de definir clases.

La incrustación es una forma ligeramente diferente de herencia. En Go, un tipo puede incluir otros tipos dentro de su definición. Esto permite aprovechar el comportamiento de los tipos incrustados, al tiempo que cada tipo puede tener su propio comportamiento adicional. La incrustación es una herramienta poderosa que ayuda a simplificar la programación orientada a objetos.

Las interfaces son una de las principales características de Go. Permiten definir un contrato para los tipos que las implementan. Esto proporciona una forma estandarizada de tratar diferentes tipos de objetos de la misma manera. Las interfaces presentan una forma clara de definir la funcionalidad de los objetos sin necesidad de tener una jerarquía de tipos explícita.

En resumen, Go es un lenguaje de programación orientado a objetos que soporta la herencia, la incrustación y las interfaces. La herencia permite la composición de tipos y métodos, la incrustación permite la reutilización de comportamiento y las interfaces definen un contrato para los tipos que las implementan. Estas características hacen de Go un lenguaje de programación orientado a objetos moderno y poderoso.

Ejercicios propuestos

A continuación te dejo 10 ejercicios más para que practiques las estructuras en Go y envíes la solución como un Pull request a este repositorio: Go Para Principiantes.

  1. Crea una interfaz en Go llamada Shape con los métodos Area() y Perimeter().
  2. Crea dos tipos en Go, Rectangle y Square, que implementen la interfaz Shape.
  3. Agrega algunos campos, como width y height, al struct Rectangle para calcular el área y el perímetro.
  4. Agrega un campo, side, al struct Square para calcular el área y el perímetro.
  5. Escribe un método en Rectangle que calcule el área.
  6. Escribe un método en Rectangle que calcule el perímetro.
  7. Escribe un método en Square que calcule el área.
  8. Escribe un método en Square que calcule el perímetro.
  9. Crea una instancia de cada tipo, Rectangle y Square.
  10. Llama a los métodos Area() y Perimeter() para cada instancia y verifica si los resultados son correctos.

Eso es todo, espero que este post te sea de utilidad y lo puedas aplicar a algún proyecto que tengas en mente. Déjame un comentario si te sirvió, si quieres añadir alguna opinión o si tienes alguna duda. Y recuerda que si te gustó, también puedes compartirlo usando los links a las redes sociales aquí abajo. ¡Buena suerte explorando Go!

Sebastian Gomez

Sebastian Gomez

Creador de contenido principalmente acerca de tecnología.

Leave a Reply

0 Comments

Advertisements

Related Posts

Categorias