Menu
Coddy logo textTech

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())  // 175

Composite è ideale quando devi rappresentare gerarchie parte-tutto e vuoi che i client trattino gli oggetti singoli e le composizioni in modo identico.

challenge icon

Sfida

Facile

Costruiamo 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 OrgComponent con due metodi:

    • GetSalary() int: restituisce lo stipendio totale di questo componente
    • GetName() 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 Employee con i campi Name (string) e Salary (int). Il suo metodo GetSalary() restituisce il proprio stipendio e GetName() restituisce il proprio nome.

    Crea una struct Department con un campo Name (string) e uno slice Members che contiene elementi OrgComponent. Aggiungi un metodo Add(c OrgComponent) per aggiungere membri. Il suo metodo GetSalary() dovrebbe restituire la somma degli stipendi di tutti i membri e GetName() 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
3

L’output dovrebbe essere:

Engineering: 165000

E dato:

4
Alice
50000
Bob
60000
Carol
55000
Dave
70000
2
Dev
2
0
1
QA
2
2
3
4

L’output dovrebbe essere:

QA: 125000

E dato:

2
Alice
50000
Bob
60000
0
1

L’output dovrebbe essere:

Bob: 60000

Nota 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)
}
quiz iconMettiti alla prova

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

Esercitati da solo: Compilatore Go online