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()
}