Campi esportati e non esportati
Fa parte della sezione Programmazione orientata agli oggetti del percorso GO di Coddy. Lezione 47 di 107.
In Go, l’incapsulamento a livello di struct è controllato dalle maiuscole e minuscole dei nomi dei campi. Questa semplice regola determina se i campi possono essere acceduti dall’esterno del package in cui è definita la struct.
I campi che iniziano con una lettera maiuscola sono esportati: accessibili da qualsiasi pacchetto. I campi che iniziano con una lettera minuscola sono non esportati: accessibili solo all'interno dello stesso pacchetto:
// Nel pacchetto "user"
type User struct {
Name string // Esportato - accessibile ovunque
Email string // Esportato - accessibile ovunque
password string // Non esportato - accessibile solo nel pacchetto "user"
age int // Non esportato - accessibile solo nel pacchetto "user"
}
Quando un altro pacchetto importa e usa questa struct, può interagire solo con i campi esportati:
// Nel package "main"
import "user"
func main() {
u := user.User{
Name: "Alice", // funziona
Email: "a@mail.com", // funziona
// password: "secret", // errore di compilazione: campo sconosciuto
}
fmt.Println(u.Name) // funziona
// fmt.Println(u.password) // errore di compilazione: impossibile accedere a un campo non esportato
}
Questo meccanismo protegge i dati sensibili e i dettagli interni dell’implementazione. Il campo password non può essere letto o modificato direttamente dall’esterno del pacchetto, costringendo il resto del codice a usare i metodi che fornisci. Questo è l’approccio di Go per ottenere l’occultamento dei dati: un principio fondamentale dell’incapsulamento nella programmazione orientata agli oggetti.
Sfida
FacileCostruiamo un sistema di conto bancario che dimostri l'incapsulamento tramite campi esportati e non esportati. Creerai un package che protegge i dati finanziari sensibili ed espone informazioni pubbliche sicure.
Organizzerai il codice in due file:
account.go: Crea una structBankAccountnel packagemainche modella un conto bancario reale con un'adeguata protezione dei dati:HolderName(esportato) - il nome del titolare del conto, sicuro da mostrare pubblicamenteAccountType(esportato) - il tipo di conto (ad es., "Savings", "Checking")balance(non esportato) - il saldo effettivo, che deve essere protettopin(non esportato) - il PIN del conto, un dato altamente sensibile
NewBankAccountche accetta il nome del titolare, il tipo di conto, il saldo iniziale e il PIN, quindi restituisce un puntatore a un nuovoBankAccount. Aggiungi anche un metodoGetPublicInfo() stringche restituisce il formato:Account: [HolderName] ([AccountType])main.go: Leggi i dettagli del conto dall'input, crea unBankAccountusando il costruttore e dimostra l'incapsulamento. Stampa le informazioni pubbliche usando il metodo, quindi accedi direttamente a ciascun campo esportato e stampalo su una riga separata. I campi non esportati (balanceepin) sono protetti. Puoi impostarli solo tramite il costruttore.
Verranno forniti i seguenti input:
- Riga 1: Nome del titolare del conto
- Riga 2: Tipo di conto
- Riga 3: Saldo iniziale (come float)
- Riga 4: Codice PIN
Ad esempio, dati Alice Johnson, Savings, 5000.50 e 1234, l'output dovrebbe essere:
Account: Alice Johnson (Savings)
Holder: Alice Johnson
Type: SavingsNota che il saldo e il PIN non compaiono mai nell'output: sono nascosti in modo sicuro all'interno della struct e sono accessibili solo all'interno del package. Questo è l'incapsulamento in azione: i dati sensibili sono protetti mentre le informazioni pubbliche restano accessibili.
Provalo tu
package main
import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
)
func main() {
reader := bufio.NewReader(os.Stdin)
// Leggi il nome del titolare del conto
holderName, _ := reader.ReadString('\n')
holderName = strings.TrimSpace(holderName)
// Leggi il tipo di conto
accountType, _ := reader.ReadString('\n')
accountType = strings.TrimSpace(accountType)
// Leggi il saldo iniziale
balanceStr, _ := reader.ReadString('\n')
balanceStr = strings.TrimSpace(balanceStr)
balance, _ := strconv.ParseFloat(balanceStr, 64)
// Leggi il PIN
pinStr, _ := reader.ReadString('\n')
pin := strings.TrimSpace(pinStr)
// TODO: Crea un nuovo BankAccount usando il costruttore
// account := NewBankAccount(...)
// TODO: Stampa le informazioni pubbliche usando il metodo GetPublicInfo()
// TODO: Stampa il nome del titolare accedendo direttamente al campo esportato
// fmt.Println("Holder:", ...)
// TODO: Stampa il tipo di conto accedendo direttamente al campo esportato
// fmt.Println("Type:", ...)
// Nota: balance e pin non sono esportati: non è possibile accedervi direttamente qui
// They are safely encapsulated within the struct
_ = balance // Remove this line when you use balance
_ = pin // Remove this line when you use pin
}
Questa lezione include un breve quiz. Inizia la lezione per rispondere e tenere traccia dei tuoi progressi.
Tutte le lezioni di Programmazione orientata agli oggetti
1Fondamenti di OOP in Go
File esterniWorkspace e moduli GoPacchetti e importazioniNomi esportati e non esportatiIntroduzione all’OOP in GoGli struct come classiDefinire metodi sugli structReceiver con puntatore vs receiver con valoreInizializzazione degli structFunzioni costruttriciRiepilogo - Calcolatrice semplice4Interfacce
Introduzione alle interfacceImplementazione implicitaL’interfaccia come contrattoInterfaccia vuota (any)Asserzione di tipoSwitch sul tipoComposizione di interfacceInterfacce Stringer ed ErrorRipasso - Calcolatore di forme7Incapsulamento
Campi esportati e non esportatiIncapsulamento a livello di packageMetodi Getter e SetterNascondimento delle informazioni in GoRiepilogo - Registri degli studenti10Tipi generici (Go 1.18+)
Introduzione ai tipi genericiParametri di tipoVincoli di tipoStruct genericheSoluzione alternativa per i metodi genericiRiepilogo - Collezione generica13Modelli di progettazione Parte 1
Introduzione ai modelli di progettazioneModello SingletonModello FactoryModello Abstract FactoryModello ObserverModello Strategy2Approfondimento su tipi e struct
Tipi di base e compostiDefinizioni di tipi personalizzatiTag delle structStruct anonimeStruct annidateValori zero e predefinitiRipasso - Rubrica5Composizione invece dell'ereditarietà
Perché Go non ha l'ereditarietàFondamenti dell'embedding delle structPromozione dei metodiEmbedding di più structEmbedding e aggregazione a confrontoMascheramento dei metodi embeddedRiepilogo: gerarchia dei dipendenti8Gestione degli errori e OOP
L'interfaccia errorTipi di errore personalizzatiWrapping degli errori (fmt.Errorf)Errori sentinellaerrors.Is() ed errors.As()Panic, Defer e RecoverRiepilogo - Parser di file11Libreria standard e OOP
io.Reader e io.Writersort.InterfaceInterfaccia fmt.Stringerencoding/json con le structInterfaccia http.HandlerRipasso - modelli REST API14Modelli di progettazione – Parte 2
Pattern CommandPattern AdapterPattern DecoratorPattern Template MethodPattern StatePattern CompositeMiddleware come Decorator3Puntatori e memoria
Concetti base dei puntatori in GoPuntatori a structPassaggio per valore vs per riferimentoLa funzione new()Garbage collection in GoRiepilogo - Costruttore di liste concatenate6Polimorfismo in Go
Polimorfismo tramite interfacceDuck typing in GoRegole di soddisfacimento delle interfacceCollezioni polimorficheIniezione delle dipendenzeRiepilogo - Elaboratore di pagamenti9Concorrenza e OOP
Basi delle goroutineCanali e comunicazioneCanali bufferizzati vs non bufferizzatiIstruzione selectsync.Mutex e sync.RWMutexsync.WaitGroupProgettazione di struct thread-safeRiepilogo: worker poolEsercitati da solo: Compilatore Go online