Menu
Coddy logo textTech

Encapsulamiento a nivel de paquete

Parte de la sección Programación Orientada a Objetos del Journey de GO de Coddy — lección 48 de 107.

Mientras que la lección anterior cubrió la visibilidad a nivel de campo, la encapsulación de Go en realidad opera a nivel de paquete. Todo lo que esté dentro del mismo paquete —independientemente del archivo en el que se encuentre— puede acceder a campos y funciones no exportados.

Considera un paquete con múltiples archivos:

// account/account.go
package account

type Account struct {
    balance int  // no exportado
}

func (a *Account) Deposit(amount int) {
    a.balance += amount
}
// account/helper.go
package account

func ResetAccount(a *Account) {
    a.balance = 0  // funciona - mismo paquete
}

Ambos archivos pertenecen al paquete account, por lo que helper.go puede acceder directamente al campo balance no exportado. Esto es intencional: permite dividir la lógica del paquete en varios archivos manteniendo el acceso interno.

Sin embargo, el código fuera del paquete no puede acceder a los miembros no exportados:

// main.go
package main

import "account"

func main() {
    acc := &account.Account{}
    acc.Deposit(100)        // funciona - método exportado
    // acc.balance = 50     // error - campo no exportado
    // account.ResetAccount // funcionaría si ResetAccount estuviera exportado
}

Este límite a nivel de paquete es el mecanismo de encapsulación principal de Go. Diseñas los paquetes como unidades cohesivas donde el código interno colabora libremente, mientras que el código externo interactúa solo a través de la API exportada. Piensa en cada paquete como un módulo autónomo con una interfaz pública clara.

challenge icon

Desafío

Fácil

Vamos a construir un sistema de gestión de inventario que demuestre cómo múltiples archivos dentro del mismo paquete pueden colaborar accediendo directamente a campos no exportados, mientras mantienen esos datos ocultos para el código externo.

Crearás tres archivos que trabajan juntos en el paquete main:

  • product.go: Define una estructura Product con un campo exportado Name y un campo no exportado quantity (int). Añade un método exportado Info() string que devuelva el formato: [Name]: [quantity] in stock
  • inventory.go: Crea funciones auxiliares que trabajen con productos accediendo directamente al campo no exportado quantity (posible porque están en el mismo paquete). Implementa:
    • Restock(p *Product, amount int) - añade la cantidad al quantity del producto
    • Sell(p *Product, amount int) bool - resta la cantidad de quantity si existe stock suficiente, devuelve true si tiene éxito o false si no hay suficiente stock
    • NewProduct(name string, initialQty int) *Product - constructor que crea un producto con el nombre y la cantidad inicial dados
  • main.go: Lee los detalles del producto de la entrada, crea un producto usando el constructor, realiza operaciones de inventario y muestra los resultados. Después de cada operación, imprime la información del producto para mostrar el estado actualizado.

Se proporcionarán las siguientes entradas:

  • Línea 1: Nombre del producto
  • Línea 2: Cantidad inicial (entero)
  • Línea 3: Cantidad a reponer (entero)
  • Línea 4: Cantidad a vender (entero)

Después de crear el producto, imprime su información inicial. Luego repón el stock e imprime la información de nuevo. Finalmente, intenta vender e imprime la información actualizada (si tiene éxito) o Sale failed: insufficient stock (si no hay suficiente cantidad).

Por ejemplo, dados Laptop, 10, 5, y 8, tu salida debería ser:

Laptop: 10 in stock
Laptop: 15 in stock
Laptop: 7 in stock

Y dados Phone, 3, 2, y 10, tu salida debería ser:

Phone: 3 in stock
Phone: 5 in stock
Sale failed: insufficient stock

La idea clave aquí es que inventory.go puede leer y modificar directamente el campo no exportado quantity porque está en el mismo paquete que product.go. Esto es la encapsulación a nivel de paquete en acción: la colaboración interna ocurre libremente mientras los datos permanecen protegidos de paquetes externos.

Hoja de referencia

El encapsulamiento en Go opera a nivel de paquete, no a nivel de archivo. Todos los archivos dentro del mismo paquete pueden acceder a campos y funciones no exportados, independientemente del archivo en el que estén definidos.

Acceso a Nivel de Paquete

Los miembros no exportados son accesibles en todos los archivos del mismo paquete:

// account/account.go
package account

type Account struct {
    balance int  // unexported
}

func (a *Account) Deposit(amount int) {
    a.balance += amount
}
// account/helper.go
package account

func ResetAccount(a *Account) {
    a.balance = 0  // works - same package
}

Restricciones de Paquetes Externos

El código fuera del paquete no puede acceder a los miembros no exportados:

// main.go
package main

import "account"

func main() {
    acc := &account.Account{}
    acc.Deposit(100)        // works - exported method
    // acc.balance = 50     // error - unexported field
}

Este diseño le permite dividir la lógica del paquete en varios archivos mientras mantiene el acceso interno, creando unidades cohesivas con interfaces públicas claras.

Pruébalo tú mismo

package main

import (
	"bufio"
	"fmt"
	"os"
	"strconv"
	"strings"
)

func main() {
	reader := bufio.NewReader(os.Stdin)

	// Leer el nombre del producto
	name, _ := reader.ReadString('\n')
	name = strings.TrimSpace(name)

	// Leer la cantidad inicial
	initialQtyStr, _ := reader.ReadString('\n')
	initialQty, _ := strconv.Atoi(strings.TrimSpace(initialQtyStr))

	// Leer la cantidad de reabastecimiento
	restockStr, _ := reader.ReadString('\n')
	restockAmount, _ := strconv.Atoi(strings.TrimSpace(restockStr))

	// Leer la cantidad de venta
	sellStr, _ := reader.ReadString('\n')
	sellAmount, _ := strconv.Atoi(strings.TrimSpace(sellStr))

	// TODO: Crear un nuevo producto usando el constructor NewProduct

	// TODO: Imprimir la información inicial del producto

	// TODO: Reabastecer el producto e imprimir la información

	// TODO: Intentar vender e imprimir el resultado
	// Si la venta tiene éxito, imprimir la información actualizada
	// Si la venta falla, imprimir "Sale failed: insufficient stock"

	fmt.Println() // Reemplazar con la salida real
}
quiz iconPonte a prueba

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