Pattern Composite
Fa parte della sezione Programmazione orientata agli oggetti del percorso GO di Coddy. Lezione 96 di 107.
Il pattern Composite ti consente di trattare in modo uniforme i singoli oggetti e i gruppi di oggetti. Mentre State modifica il comportamento in base allo stato interno, Composite crea strutture ad albero in cui sia le foglie sia i contenitori condividono la stessa interfaccia.
Questo schema è perfetto per strutture gerarchiche come i file system, gli organigrammi o i componenti dell’interfaccia utente. In Go, definiamo un’interfaccia comune che implementano sia i singoli elementi sia i contenitori:
type Component interface {
GetSize() int
}
type File struct {
Name string
Size int
}
func (f File) GetSize() int {
return f.Size
}Il composito (contenitore) contiene elementi figli e implementa la stessa interfaccia aggregando i loro risultati:
type Folder struct {
Name string
Children []Component
}
func (f *Folder) Add(c Component) {
f.Children = append(f.Children, c)
}
func (f Folder) GetSize() int {
total := 0
for _, child := range f.Children {
total += child.GetSize()
}
return total
}Ora puoi annidare cartelle all'interno di altre cartelle e chiamare GetSize() funziona allo stesso modo sia per un singolo file sia per un intero albero di directory:
docs := &Folder{Name: "docs"}
docs.Add(File{Name: "readme.txt", Size: 100})
docs.Add(File{Name: "notes.txt", Size: 50})
root := &Folder{Name: "root"}
root.Add(docs)
root.Add(File{Name: "config.json", Size: 25})
fmt.Println(root.GetSize()) // 175Composite è ideale quando devi rappresentare gerarchie parte-tutto e vuoi che i client trattino gli oggetti singoli e le composizioni in modo identico.
Sfida
FacileCostruiamo un sistema di organigrammi usando il pattern Composite! Creerai una struttura in cui sia i singoli dipendenti sia i reparti (che contengono dipendenti o altri reparti) possono essere trattati in modo uniforme, perfetto per calcolare gli stipendi totali di qualsiasi parte dell’organizzazione.
Organizzerai il codice in tre file:
component.go: Definisci l’interfaccia comune che sia i singoli elementi sia i gruppi implementeranno.Crea un’interfaccia
OrgComponentcon due metodi:GetSalary() int: restituisce lo stipendio totale di questo componenteGetName() string: restituisce il nome del dipendente o del reparto
organization.go: Implementa sia i tipi foglia (singoli dipendenti) sia i tipi compositi (reparti).Crea una struct
Employeecon i campiName(string) eSalary(int). Il suo metodoGetSalary()restituisce il proprio stipendio eGetName()restituisce il proprio nome.Crea una struct
Departmentcon un campoName(string) e uno sliceMembersche contiene elementiOrgComponent. Aggiungi un metodoAdd(c OrgComponent)per aggiungere membri. Il suo metodoGetSalary()dovrebbe restituire la somma degli stipendi di tutti i membri eGetName()restituisce il nome del reparto.main.go: Crea una struttura organizzativa e calcola gli stipendi.Leggi il numero di dipendenti. Per ogni dipendente, leggi il nome e lo stipendio e crea un’istanza di
Employee. Poi leggi il numero di reparti. Per ogni reparto, leggi il nome e il numero di indici dei membri, seguiti da quegli indici (a partire da 0, riferiti ai dipendenti o ai reparti creati in precedenza, nell’ordine di creazione).Dopo aver creato la struttura, leggi un indice finale e stampa il nome di quel componente e lo stipendio totale nel formato:
[name]: [salary]
Verranno forniti i seguenti input:
- Numero di dipendenti, seguito dal nome e dallo stipendio di ogni dipendente su righe separate
- Numero di reparti, seguito dal nome di ogni reparto, dal numero di membri e dagli indici dei membri
- Indice finale da interrogare
Ad esempio, dato:
3
Alice
50000
Bob
60000
Carol
55000
1
Engineering
3
0
1
2
3L’output dovrebbe essere:
Engineering: 165000E dato:
4
Alice
50000
Bob
60000
Carol
55000
Dave
70000
2
Dev
2
0
1
QA
2
2
3
4L’output dovrebbe essere:
QA: 125000E dato:
2
Alice
50000
Bob
60000
0
1L’output dovrebbe essere:
Bob: 60000Nota come GetSalary() funzioni in modo identico sia quando lo chiami su un singolo dipendente sia quando lo chiami su un intero reparto: il pattern Composite ti permette di trattare singoli elementi e gruppi in modo uniforme tramite la stessa interfaccia!
Provalo tu
package main
import "fmt"
func main() {
// Leggi il numero di dipendenti
var numEmployees int
fmt.Scanln(&numEmployees)
// Memorizza tutti i componenti (dipendenti e reparti) nell'ordine di creazione
var components []OrgComponent
// TODO: Leggi il nome e lo stipendio di ogni dipendente
// Crea istanze di Employee e aggiungile allo slice components
for i := 0; i < numEmployees; i++ {
var name string
var salary int
fmt.Scanln(&name)
fmt.Scanln(&salary)
// TODO: Crea un dipendente e aggiungilo a components
}
// Leggi il numero di reparti
var numDepartments int
fmt.Scanln(&numDepartments)
// TODO: Leggi il nome, il numero di membri e gli indici dei membri di ogni reparto
// Crea istanze di Department, aggiungi i membri tramite indice e aggiungile a components
for i := 0; i < numDepartments; i++ {
var deptName string
var memberCount int
fmt.Scanln(&deptName)
fmt.Scanln(&memberCount)
// TODO: Crea un reparto, leggi gli indici dei membri, aggiungi i membri e aggiungi il reparto a components
}
// Leggi l'indice finale da interrogare
var queryIndex int
fmt.Scanln(&queryIndex)
// TODO: Stampa il nome e lo stipendio totale del componente nel formato: [name]: [salary]
// Esempio: fmt.Printf("%s: %d\n", name, salary)
}
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