1. Cos'è il pattern composito
Il Pattern Composito è un pattern di progettazione strutturale degli oggetti comunemente utilizzato. Combina gli oggetti in una struttura ad albero per rappresentare una relazione gerarchica "parte-tutto", consentendo ai clienti di gestire singoli oggetti e composizioni di oggetti in modo coerente.
2. Caratteristiche e vantaggi del pattern composito
I principali vantaggi del pattern composito sono:
- Può definire chiaramente gli oggetti complessi gerarchici, rappresentando tutta o parte della gerarchia degli oggetti, semplificando l'aggiunta di nuovi componenti.
- Fornisce un'interfaccia unificata, rendendo l'accesso alle parti composite e agli oggetti singoli coerente, consentendo ai clienti di utilizzare uniformemente gli oggetti singoli e quelli compositi.
3. Scenari di applicazione del pattern composito
- Quando si desidera rappresentare la gerarchia "parte-tutto" degli oggetti.
- Quando si desidera che i client ignorino le differenze tra gli oggetti compositi e gli oggetti singoli, e utilizzino uniformemente tutti gli oggetti nella struttura composita.
4. Implementazione del pattern composito in Golang
Supponiamo di dover progettare un’applicazione di e-commerce, il catalogo prodotti è un buon esempio del pattern composito. Una categoria può contenere altre categorie e anche prodotti (ad esempio la categoria elettronica contiene telefoni, computer, e i telefoni contengono iPhone, Samsung, ecc.).
4.1 Diagramma delle Classi UML
4.2 Introduzione dell'esempio
In questo esempio, abbiamo Component
(ovviamente utilizzando la caratteristica dell'interfaccia dell'orientamento agli oggetti) come classe base astratta e sia Composite
che Leaf
implementano questa interfaccia, rappresentando oggetti contenitore e oggetti base, rispettivamente.
4.3 Passo 1 dell'implementazione: Definire la classe component astratta
4.3.1 Definire l'interfaccia della classe componente astratta
// Component: Interfaccia del componente di base, definisce la comunalità dei gruppi e degli individui
type Component interface {
Cerca(string)
}
4.3.2 Implementare i metodi di base della classe componente astratta
Questo passaggio è implementato specificamente nella classe componente contenitore e nella classe componente foglia.
4.4 Passo 2 dell'implementazione: Definire la classe componente foglia
Questa classe concreta rappresenta la categoria o l'oggetto di fondo nella gerarchia e non ha la prossima struttura di oggetti.
4.4.1 Ereditare dalla classe componente astratta
In Go, l'ereditarietà dell'interfaccia è implementata dal modo in cui i metodi sono implementati attraverso Struct.
4.4.2 Implementare i metodi specifici della classe componente foglia
// Prodotto: Rappresenta un nodo foglia, cioè un prodotto, e non può avere nodi figlio
type Prodotto struct {
Nome string
}
// Cerca: Cerca i prodotti
func (p *Prodotto) Cerca(parolaChiave string) {
if strings.Contains(p.Nome, parolaChiave) {
fmt.Printf("Prodotto: '%s' contiene la parola chiave: '%s'\n", p.Nome, parolaChiave)
}
}
4.5 Passo 3 dell'implementazione: Definire la classe componente contenitore
Questa classe è utilizzata per memorizzare e gestire gli oggetti figlio e include generalmente alcuni metodi per gestire e organizzare gli oggetti figlio, come aggiungi(Componente)
, rimuovi(Componente)
, ecc.
4.5.1 Eredità dalla classe componente astratta
Anche questo è implementato utilizzando Struct per implementare i metodi dell'interfaccia in Go.
4.5.2 Implementazione dei metodi specifici della classe componente contenitore
// Categoria: Rappresenta un nodo contenitore, cioè una categoria di prodotti, che può avere nodi figlio
type Categoria struct {
Nome string
Figli []Component
}
// Aggiungi: Aggiunge un nodo figlio
func (c *Categoria) Aggiungi(figlio Component) {
c.Figli = append(c.Figli, figlio)
}
// Rimuovi: Rimuove un nodo figlio
func (c *Categoria) Rimuovi(figlio Component) {
// Implementazione specifica omessa
}
// Cerca: Cerca i prodotti
func (c *Categoria) Cerca(parolaChiave string) {
fmt.Printf("Categoria: %s\n", c.Nome)
for _, composito := range c.Figli {
composito.Cerca(parolaChiave)
}
}
4.6 Implementazione Passo 4: Esempio di Codice Client
Istanzia una struttura, assemblala in una struttura ad albero, e quindi chiama l'operazione di accesso della struttura ad albero.
func main() {
root := &Category{Name: "Radice"}
elettronica := &Category{Name: "Elettronica"}
telefono := &Product{Name: "Telefono"}
tv := &Product{Name: "Televisione"}
root.Add(elettronica)
elettronica.Add(telefono)
elettronica.Add(tv)
root.Search("telefono") // Questo controllerà in tutti i figli
}