Campos exportados vs. no exportados
Parte de la sección Programación Orientada a Objetos del Journey de GO de Coddy. Lección 47 de 107.
En Go, la encapsulación a nivel de estructura se controla mediante el uso de mayúsculas y minúsculas en los nombres de los campos. Esta sencilla regla determina si se puede acceder a los campos desde fuera del paquete en el que se define la estructura.
Los campos que comienzan con una letra mayúscula son exportados: accesibles desde cualquier paquete. Los campos que comienzan con una letra minúscula son no exportados: solo accesibles dentro del mismo paquete:
// En el paquete "user"
type User struct {
Name string // Exportado - accesible en todas partes
Email string // Exportado - accesible en todas partes
password string // No exportado - solo accesible en el paquete "user"
age int // No exportado - solo accesible en el paquete "user"
}
Cuando otro paquete importa y utiliza esta estructura, solo puede interactuar con campos exportados:
// En el paquete "main"
import "user"
func main() {
u := user.User{
Name: "Alice", // funciona
Email: "a@mail.com", // funciona
// password: "secret", // error de compilación: campo desconocido
}
fmt.Println(u.Name) // funciona
// fmt.Println(u.password) // error de compilación: no se puede referir a un campo no exportado
}
Este mecanismo protege los datos confidenciales y los detalles de implementación internos. El campo password no se puede leer ni modificar directamente desde fuera del paquete, lo que obliga a otro código a utilizar los métodos que proporcionas. Este es el enfoque de Go para lograr la ocultación de datos: un principio fundamental de la encapsulación en la programación orientada a objetos.
Desafío
FácilConstruyamos un sistema de cuentas bancarias que demuestre la encapsulación mediante campos exportados y no exportados. Crearás un paquete que proteja los datos financieros confidenciales y, al mismo tiempo, exponga información pública segura.
Organizarás tu código en dos archivos:
account.go: Crea una estructuraBankAccounten el paquetemainque modele una cuenta bancaria real con la protección adecuada de datos:HolderName(exportado): el nombre del titular de la cuenta, seguro para mostrar públicamenteAccountType(exportado): el tipo de cuenta (por ejemplo, "Savings", "Checking")balance(no exportado): el saldo real, que debe estar protegidopin(no exportado): el PIN de la cuenta, un dato altamente confidencial
NewBankAccountque reciba el nombre del titular, el tipo de cuenta, el saldo inicial y el PIN, y después devuelva un puntero a un nuevoBankAccount. Añade también un métodoGetPublicInfo() stringque devuelva el formato:Account: [HolderName] ([AccountType])main.go: Lee los detalles de la cuenta desde la entrada, crea unBankAccountusando la función constructora y demuestra la encapsulación. Imprime la información pública mediante el método y, después, accede directamente a cada campo exportado y lo imprime en líneas separadas. Los campos no exportados (balanceypin) están protegidos. Solo puedes establecerlos mediante la función constructora.
Se proporcionarán las siguientes entradas:
- Línea 1: Nombre del titular de la cuenta
- Línea 2: Tipo de cuenta
- Línea 3: Saldo inicial (como un número de tipo float)
- Línea 4: Código PIN
Por ejemplo, dadas Alice Johnson, Savings, 5000.50 y 1234, tu salida debería ser:
Account: Alice Johnson (Savings)
Holder: Alice Johnson
Type: SavingsObserva que el saldo y el PIN nunca aparecen en la salida: están ocultos de forma segura dentro de la estructura y solo son accesibles dentro del paquete. Esto es la encapsulación en acción: los datos confidenciales están protegidos mientras la información pública sigue siendo accesible.
Pruébalo tú mismo
package main
import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
)
func main() {
reader := bufio.NewReader(os.Stdin)
// Lee el nombre del titular de la cuenta
holderName, _ := reader.ReadString('\n')
holderName = strings.TrimSpace(holderName)
// Lee el tipo de cuenta
accountType, _ := reader.ReadString('\n')
accountType = strings.TrimSpace(accountType)
// Lee el saldo inicial
balanceStr, _ := reader.ReadString('\n')
balanceStr = strings.TrimSpace(balanceStr)
balance, _ := strconv.ParseFloat(balanceStr, 64)
// Lee el PIN
pinStr, _ := reader.ReadString('\n')
pin := strings.TrimSpace(pinStr)
// TODO: Crea un nuevo BankAccount usando el constructor
// account := NewBankAccount(...)
// TODO: Imprime la información pública usando el método GetPublicInfo()
// TODO: Imprime el nombre del titular accediendo directamente al campo exportado
// fmt.Println("Holder:", ...)
// TODO: Imprime el tipo de cuenta accediendo directamente al campo exportado
// fmt.Println("Type:", ...)
// Note: balance y pin no están exportados - no se puede acceder a ellos directamente aquí
// Están encapsulados de forma segura dentro de la struct
_ = balance // Elimina esta línea cuando uses balance
_ = pin // Elimina esta línea cuando uses pin
}
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 PoolPractica por tu cuenta: Compilador de Go online