Incapsulamento a livello di package
Fa parte della sezione Programmazione orientata agli oggetti del percorso GO di Coddy. Lezione 48 di 107.
Mentre la lezione precedente trattava la visibilità a livello di campo, l’incapsulamento di Go opera in realtà a livello di pacchetto. Tutto ciò che si trova nello stesso pacchetto, indipendentemente dal file in cui si trova, può accedere ai campi e alle funzioni non esportati.
Considera un pacchetto con più file:
// account/account.go
package account
type Account struct {
balance int // non esportato
}
func (a *Account) Deposit(amount int) {
a.balance += amount
}
// account/helper.go
package account
func ResetAccount(a *Account) {
a.balance = 0 // funziona - stesso package
}
Entrambi i file appartengono al pacchetto account, quindi helper.go può accedere direttamente al campo non esportato balance. È intenzionale. Ti permette di suddividere la logica del pacchetto tra più file mantenendo l'accesso interno.
Tuttavia, il codice esterno al pacchetto non può accedere ai membri non esportati:
// main.go
package main
import "account"
func main() {
acc := &account.Account{}
acc.Deposit(100) // funziona - metodo esportato
// acc.balance = 50 // errore - campo non esportato
// account.ResetAccount // funzionerebbe se ResetAccount fosse esportato
}
Questo confine a livello di pacchetto è il principale meccanismo di incapsulamento di Go. Progetti i pacchetti come unità coese, in cui il codice interno collabora liberamente, mentre il codice esterno interagisce solo tramite l’API esportata. Considera ogni pacchetto come un modulo autonomo con un’interfaccia pubblica chiara.
Sfida
FacileCostruiamo un sistema di gestione dell'inventario che mostra come più file all'interno dello stesso package possano collaborare accedendo direttamente ai campi non esportati, mantenendo al contempo quei dati nascosti al codice esterno.
Creerai tre file che lavorano insieme nel package main:
product.go: Definisci una structProductcon un campo esportatoNamee un campo non esportatoquantity(int). Aggiungi un metodo esportatoInfo() stringche restituisce il formato:[Name]: [quantity] in stockinventory.go: Crea funzioni di supporto che lavorano con i prodotti accedendo direttamente al campo non esportatoquantity(possibile perché si trovano nello stesso package). Implementa:Restock(p *Product, amount int)- aggiunge l'importo alla quantità del prodottoSell(p *Product, amount int) bool- sottrae l'importo dalla quantità se le scorte sono sufficienti; restituiscetruein caso di successo ofalsese le scorte non sono sufficientiNewProduct(name string, initialQty int) *Product- costruttore che crea un prodotto con il nome e la quantità iniziale specificati
main.go: Leggi i dettagli del prodotto dall'input, crea un prodotto usando il costruttore, esegui le operazioni sull'inventario e visualizza i risultati. Dopo ogni operazione, stampa le informazioni sul prodotto per mostrare lo stato aggiornato.
Verranno forniti i seguenti input:
- Riga 1: Nome del prodotto
- Riga 2: Quantità iniziale (intero)
- Riga 3: Quantità da rifornire (intero)
- Riga 4: Quantità da vendere (intero)
Dopo aver creato il prodotto, stampa le sue informazioni iniziali. Poi rifornisci le scorte e stampa di nuovo le informazioni. Infine, prova a vendere e stampa le informazioni aggiornate (se l'operazione riesce) oppure Sale failed: insufficient stock (se la quantità non è sufficiente).
Ad esempio, dati Laptop, 10, 5 e 8, l'output dovrebbe essere:
Laptop: 10 in stock
Laptop: 15 in stock
Laptop: 7 in stockE dati Phone, 3, 2 e 10, l'output dovrebbe essere:
Phone: 3 in stock
Phone: 5 in stock
Sale failed: insufficient stockL'idea chiave è che inventory.go può leggere e modificare direttamente il campo non esportato quantity perché si trova nello stesso package di product.go. Questo è un esempio di incapsulamento a livello di package: la collaborazione interna avviene liberamente, mentre i dati rimangono protetti dai package esterni.
Provalo tu
package main
import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
)
func main() {
reader := bufio.NewReader(os.Stdin)
// Leggi il nome del prodotto
name, _ := reader.ReadString('\n')
name = strings.TrimSpace(name)
// Leggi la quantità iniziale
initialQtyStr, _ := reader.ReadString('\n')
initialQty, _ := strconv.Atoi(strings.TrimSpace(initialQtyStr))
// Leggi la quantità da aggiungere alle scorte
restockStr, _ := reader.ReadString('\n')
restockAmount, _ := strconv.Atoi(strings.TrimSpace(restockStr))
// Leggi la quantità da vendere
sellStr, _ := reader.ReadString('\n')
sellAmount, _ := strconv.Atoi(strings.TrimSpace(sellStr))
// TODO: Crea un nuovo prodotto usando il costruttore NewProduct
// TODO: Stampa le informazioni iniziali del prodotto
// TODO: Rifornisci il prodotto e stampa le informazioni
// TODO: Prova a vendere il prodotto e stampa il risultato
// Se la vendita riesce, stampa le informazioni aggiornate
// Se la vendita non riesce, stampa "Sale failed: insufficient stock"
fmt.Println() // Sostituisci con l'output effettivo
}
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