Métodos Getter y Setter
Parte de la sección Programación Orientada a Objetos del Journey de GO de Coddy — lección 49 de 107.
Dado que los campos no exportados no pueden ser accedidos directamente desde fuera de un paquete, es necesario proporcionar métodos que permitan un acceso controlado. Estos son comúnmente llamados getters y setters.
En Go, la convención para los getters es sencilla: usa el nombre del campo con la primera letra en mayúscula. No uses el prefijo "Get"; simplemente usa Balance(), no GetBalance():
type BankAccount struct {
balance int // no exportado
}
// Getter - devuelve el valor
func (b *BankAccount) Balance() int {
return b.balance
}
// Setter - modifica el valor con validación
func (b *BankAccount) SetBalance(amount int) {
if amount >= 0 {
b.balance = amount
}
}
El verdadero poder de los setters es añadir validación o lógica de negocio. En lugar de permitir que el código externo establezca cualquier valor directamente, tú controlas lo que está permitido:
func (b *BankAccount) Deposit(amount int) bool {
if amount <= 0 {
return false
}
b.balance += amount
return true
}
Este método Deposit actúa como un setter especializado que impone reglas: no se permiten depósitos negativos. El código externo debe pasar a través de tus métodos, asegurando que el struct siempre permanezca en un estado válido. Esto es la encapsulación en acción: ocultar los datos mientras se expone un comportamiento controlado.
Desafío
FácilVamos a construir un convertidor de temperatura que demuestre una encapsulación adecuada utilizando métodos getter y setter. Crearás un struct que almacena la temperatura internamente en Celsius pero proporciona acceso controlado a través de métodos con validación.
Organizarás tu código en dos archivos:
temperature.go: Crea un structTemperaturecon un campocelsiusno exportado (float64). Implementa los siguientes métodos:Celsius()- un getter que devuelve la temperatura actual en CelsiusSetCelsius(value float64)- un setter que solo acepta valores iguales o superiores al cero absoluto (-273.15). Si el valor está por debajo de este umbral, la temperatura debe permanecer sin cambios.Fahrenheit()- devuelve la temperatura convertida a Fahrenheit utilizando la fórmula:(celsius * 9/5) + 32SetFahrenheit(value float64)- convierte la entrada de Fahrenheit a Celsius y la almacena, pero solo si el valor Celsius resultante es igual o superior a -273.15
NewTemperature(celsius float64) *Temperatureque crea una temperatura inicializada al valor dado (asume una entrada válida para el constructor).main.go: Lee los valores de temperatura de la entrada y demuestra los métodos getter/setter. Crea una temperatura, muestra sus valores en ambas unidades, luego intenta establecer nuevos valores y muestra los resultados.
Se proporcionarán las siguientes entradas:
- Línea 1: Valor Celsius inicial
- Línea 2: Nuevo valor Celsius a establecer
- Línea 3: Nuevo valor Fahrenheit a establecer
Formatea todas las salidas de temperatura con dos decimales. Imprime la siguiente secuencia:
- Después de la creación:
C: [celsius] F: [fahrenheit] - Después de establecer Celsius:
C: [celsius] F: [fahrenheit] - Después de establecer Fahrenheit:
C: [celsius] F: [fahrenheit]
Por ejemplo, dados 25, -300, y 212, tu salida debería ser:
C: 25.00 F: 77.00
C: 25.00 F: 77.00
C: 100.00 F: 212.00Observa que la segunda línea muestra valores sin cambios porque -300 está por debajo del cero absoluto, por lo que el setter lo rechazó. La tercera línea muestra la temperatura después de establecer con éxito 212°F (que se convierte a 100°C). Este es el poder de los setters: imponen reglas que mantienen tus datos válidos.
Hoja de referencia
Los campos no exportados requieren métodos getter y setter para un acceso controlado desde fuera del paquete.
Convención de Getters
En Go, los getters utilizan el nombre del campo con la primera letra en mayúscula. No use el prefijo "Get":
type BankAccount struct {
balance int // unexported
}
// Getter - returns the value
func (b *BankAccount) Balance() int {
return b.balance
}Setter con Validación
Los setters modifican valores y pueden incluir validación o lógica de negocio:
// Setter - modifies the value with validation
func (b *BankAccount) SetBalance(amount int) {
if amount >= 0 {
b.balance = amount
}
}Setters Especializados
Cree métodos que impongan reglas específicas en lugar de permitir la asignación directa de valores:
func (b *BankAccount) Deposit(amount int) bool {
if amount <= 0 {
return false
}
b.balance += amount
return true
}Este enfoque garantiza el encapsulamiento: ocultar datos mientras se expone un comportamiento controlado, manteniendo los structs en un estado válido.
Pruébalo tú mismo
package main
import (
"fmt"
)
func main() {
// Leer los valores de entrada
var initialCelsius float64
var newCelsius float64
var newFahrenheit float64
fmt.Scanln(&initialCelsius)
fmt.Scanln(&newCelsius)
fmt.Scanln(&newFahrenheit)
// TODO: Crear una nueva Temperature usando el constructor
// TODO: Imprimir los valores iniciales en el formato "C: [celsius] F: [fahrenheit]" con 2 decimales
// TODO: Intentar establecer el nuevo valor de Celsius e imprimir el resultado
// TODO: Intentar establecer el nuevo valor de Fahrenheit e imprimir el resultado
}
Esta lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.
Todas las lecciones de Programación Orientada a Objetos
1Fundamentos de POO en Go
Archivos externosEspacio de trabajo y módulos de GoPaquetes e importacionesNombres exportados vs. no exportadosIntroducción a la POO en GoStructs como clasesDefinición de métodos en structsReceptores de puntero vs. de valorInicialización de structsFunciones constructorasResumen - Calculadora simple4Interfaces
Introducción a las interfacesImplementación implícitaLa interfaz como contratoInterfaz vacía (any)Type AssertionType SwitchComposición de interfacesInterfaces Stringer y ErrorResumen: Calculadora de figuras7Encapsulamiento
Campos exportados vs. no exportadosEncapsulamiento a nivel de paqueteMétodos Getter y SetterOcultamiento de información en GoResumen - Registros de estudiantes10Genéricos (Go 1.18+)
Introducción a los GenéricosParámetros de tipoRestricciones de tipoStructs genéricosSolución alternativa para métodos genéricosResumen - Colección genérica13Patrones de diseño - Parte 1
Introducción a los patrones de diseñoPatrón SingletonPatrón FactoryPatrón Abstract FactoryPatrón ObserverPatrón Strategy2Inmersión profunda en Tipos y Structs
Tipos básicos y compuestosDefiniciones de tipos personalizadosEtiquetas de StructsStructs anónimosStructs anidadosValores cero y por defectoRepaso - Libreta de contactos5Composición sobre herencia
Por qué Go no tiene herenciaConceptos básicos de incrustación de structsPromoción de métodosIncrustación de múltiples structsIncrustación vs AgregaciónShadowing de métodos incrustadosResumen - Jerarquía de empleados8Manejo de Errores y OOP
La interfaz errorTipos de errores personalizadosEnvoltura de errores (fmt.Errorf)Errores centinelaerrors.Is() y errors.As()Panic, Defer y RecoverResumen - Analizador de archivos3Punteros y Memoria
Fundamentos de punteros en GoPunteros a StructsPaso por valor vs referenciaLa función new()Garbage Collection en GoResumen - Constructor de Linked List6Polimorfismo en Go
Polimorfismo mediante interfacesDuck Typing en GoReglas de satisfacción de interfacesColecciones polimórficasInyección de dependenciasResumen - Procesador de pagos9Concurrencia y POO
Fundamentos de GoroutinesChannels y comunicaciónChannels con buffer vs. sin bufferSentencia Selectsync.Mutex y sync.RWMutexsync.WaitGroupDiseño de estructuras Thread-SafeResumen - Worker Pool