El lenguaje Go no es orientado a objetos puro, pero define varios de sus principios.
En el concepto anterior vimos que a un struct se le pueden asociar métodos que manipulan sus campos (concepto de encapsulamiento), ahora veremos que un struct puede definir campos que heredan de otro struct todos sus campos y métodos.
Esto se puede comparar con el concepto de herencia de los lenguajes orientados a objetos.
Veremos con un ejemplo como se define un campo anónimo.
package main import "fmt" type Persona struct { nombre string edad int } func (per *Persona) cargar() { fmt.Print("Ingrese el nombre:") fmt.Scan(&per.nombre) fmt.Print("Ingrese la edad:") fmt.Scan(&per.edad) } func (per Persona) imprimir() { fmt.Println("Datos Personales") fmt.Println("***** **********") fmt.Println("Nombre:", per.nombre) fmt.Println("Edad:", per.edad) } func (per Persona) mayorEdad() { if per.edad >= 18 { fmt.Println(per.nombre, "es mayor de edad") } else { fmt.Println(per.nombre, "no es mayor de edad") } } type Empleado struct { Persona sueldo int } func (emp *Empleado) cargar() { fmt.Println("Ingreso de datos del empleado") emp.Persona.cargar() fmt.Print("Ingrese el sueldo:") fmt.Scan(&emp.sueldo) } func (emp Empleado) imprimir() { emp.Persona.imprimir() fmt.Println("Sueldo:", emp.sueldo) } func (emp Empleado) pagaImpuestos() { if emp.sueldo >= 300 { fmt.Println(emp.nombre, "debe pagar impuestos") } else { fmt.Println(emp.nombre, "no debe pagar impuestos") } } func main() { var persona1 Persona persona1.cargar() persona1.imprimir() persona1.mayorEdad() var empleado1 Empleado empleado1.cargar() empleado1.imprimir() empleado1.pagaImpuestos() }
El problema representa dos conceptos, por un lado tenemos declarado el struct Persona y una serie de métodos asociados a este struct que nos permiten cargar su nombre y edad, imprimir los datos y mostrar un mensaje si es mayor de edad:
type Persona struct { nombre string edad int } func (per *Persona) cargar() { fmt.Print("Ingrese el nombre:") fmt.Scan(&per.nombre) fmt.Print("Ingrese la edad:") fmt.Scan(&per.edad) } func (per Persona) imprimir() { fmt.Println("Datos Personales") fmt.Println("***** **********") fmt.Println("Nombre:", per.nombre) fmt.Println("Edad:", per.edad) } func (per Persona) mayorEdad() { if per.edad >= 18 { fmt.Println(per.nombre, "es mayor de edad") } else { fmt.Println(per.nombre, "no es mayor de edad") } }
En la main definimos un struct de tipo Persona y llamamos a sus métodos:
func main() { var persona1 Persona persona1.cargar() persona1.imprimir() persona1.mayorEdad()
Este problema lo vimos en el concepto anterior al presentar el tema de métodos.
Lo nuevo aparece cuando queremos representar el concepto de Empleado y queremos definir como atributos su nombre, edad y sueldo. Como vemos un Empleado es una Persona que añade la característica que tiene sueldo. Mediante la definición de un campo anónimo en Go podemos añadir todos los campos y métodos asociados con la sintaxis:
type Empleado struct { Persona sueldo int }
Hemos definido el campo anónimo llamado Persona.
Ahora cuando definamos una variable de tipo Empleado estamos reservando espacio para todos los campos definidos en Empleado y Persona (es decir 3 campos), pero además tenemos acceso a todos los métodos asociados al struct Persona.
El struct Empleado también define sus propios métodos:
func (emp *Empleado) cargar() { fmt.Println("Ingreso de datos del empleado") emp.Persona.cargar() fmt.Print("Ingrese el sueldo:") fmt.Scan(&emp.sueldo) }
El método cargar llama al método cargar del campo anónimo Persona: emp.Persona.cargar()
El método cargar del struct Empleado extiende el método cargar del struct Persona cargando el atributo sueldo.
De forma similar para imprimir los datos del Empleado llamamos al imprimir de Persona:
func (emp Empleado) imprimir() { emp.Persona.imprimir() fmt.Println("Sueldo:", emp.sueldo) }
En el método pagarImpuestos podemos acceder al campo "nombre" sin la necesidad de anteceder el nombre del campo anónimo:
func (emp Empleado) pagaImpuestos() { if emp.sueldo >= 300 { fmt.Println(emp.nombre, "debe pagar impuestos") } else { fmt.Println(emp.nombre, "no debe pagar impuestos") } }
Podemos pero no es necesario indicar que nombre pertenece al campo anónimo:
fmt.Println(emp.Persona.nombre, "debe pagar impuestos")
En la main definimos una variable de tipo Empleado y llamamos a sus métodos:
var empleado1 Empleado empleado1.cargar() empleado1.imprimir() empleado1.pagaImpuestos()
type Usuario struct { nombre string clave string } type Administrador struct { Usuario privilegio int }Definir los siguientes métodos para el struct Usuario:
func (usu *Usuario) cargar(nombre, clave string) { func (usu Usuario) imprimir() {y para el administrador:
func (admin *Administrador) cargar(nombre, clave string, privilegio int) { func (admin Administrador) imprimirPrivilegio() {En la función main definir solo una variable de tipo Adminstrador:
func main() { var administrador1 Administrador administrador1.cargar("juan", "abc123", 1) administrador1.imprimirPrivilegio() }
ejercicio141.go package main import "fmt" type Usuario struct { nombre string clave string } type Administrador struct { Usuario privilegio int } func (usu *Usuario) cargar(nombre, clave string) { usu.nombre = nombre usu.clave = clave } func (usu Usuario) imprimir() { fmt.Println("Nombre:", usu.nombre) fmt.Println("Clave:", usu.clave) } func (admin *Administrador) cargar(nombre, clave string, privilegio int) { admin.Usuario.cargar(nombre, clave) admin.privilegio = privilegio } func (admin Administrador) imprimirPrivilegio() { admin.imprimir() fmt.Println("Privilegio: ", admin.privilegio) } func main() { var administrador1 Administrador administrador1.cargar("juan", "abc123", 1) administrador1.imprimirPrivilegio() }