Il compito che viene svolto dalle classi in altri linguaggi basati - a livelli diversi - sulla programmazione
orientata agli oggetti (pensiamo a Java e
C++ ma anche Python e
JavaScript, solo per fare qualche esempio) in Go
viene assolto per buona parte dalle struct.
Con tale costrutto possiamo definire "entità" nel nostro programma che contemporaneamente:
- daranno vita ad un nuovo tipo di dato da utilizzare all'interno del codice che stiamo scrivendo;
- aggregheranno più variabili formando una sorta di pacchetto con cui potranno essere gestiti insieme più valori di tipo diverso;
- definiranno un'informazione di più alto livello in quanto ogni struct definita ha un significato ben preciso nel programma rappresentando un concetto logico che
quest'ultimo tratta (ad esempio: gli articoli venduti in un e-commerce, le notizie trattate da un blog, i giocatori di un Fantacalcio e così via).
Il tutto ruoterà attorno ad una parola chiave principale: struct
. Vediamo subito un primo esempio.
Come definire una struct
Supponiamo che il programma che stiamo scrivendo in Go sia un gestionale per i dipendenti di una fabbrica. Dovremo trattare
le informazioni relative ad ogni singolo operaio ognuno dei quali identificato da una matricola, un nome, un cognome ed
un numero di reparto. Si tratta di dati diversi e di tipo diverso - tre stringhe ed un numero intero - ma che nel complesso definiscono
l'entità operaio, un tipo di informazione di livello più alto in quanto frutto di aggregazione di più dati.
Ecco come possiamo prepararla in Go:
type operaio struct {
matricola string
nome string
cognome string
reparto int
}
Come notiamo, abbiamo riposto tra le parentesi graffe le definizioni delle variabili che andranno a comporre
un operaio nel nostro programma. Con la struct in sè stessa abbiamo fatto l'aggregazione mentre con type
abbiamo definito
un nuovo tipo di dato che sarà valido solo in questo programma.
Dichiarare variabili del nuovo tipo
Possiamo da subito creare degli "oggetti" di tipo operaio
, con la seguente sintassi:
operaio1:=operaio{"AX12345", "Gigi", "Bianchi", 5}
Abbiamo creato la variabile operaio1
popolandola con una sequenza di valori. Si noti che abbiamo
solo elencato stringhe e numeri senza nominare le varie proprietà. Ciò funziona perchè Go prenderà in considerazione
l'ordine in cui le abbiamo specificate al momento di definire la struct ed assegnerà ogni valore
alla corrispondente variabile. In questo caso, le proprietà nella struct sono matricola
, nome
,
cognome
, reparto
pertanto Go assegnerà alla prima il primo valore (AX12345
), alla seconda il secondo valore (Gigi
) e così via.
Se ad esempio volessimo fare stampare il nome di operaio1
potremmo usare:
fmt.Println(operaio1.nome)
e questo produrrebbe la stampa della stringa Gigi. In alternativa, possiamo definire un oggetto ispirato ad una struct
specificando esplicitamente l'associazione tra valore e nome della proprietà e ciò permetterà di indicare tali dati nell'ordine che
preferiamo. Esempio:
operaio2:=operaio{nome: "Pietro", matricola: "AX12345",reparto: 1, cognome: "Arancioni"}
Su quest'ultima notazione, importante specificare che potremmo omettere l'impostazione di una o più proprietà: non riceveremmo un
errore ed in tal caso il corrispondente valore rimarrebbe vuoto. Se ad esempio non inserissimo il cognome questo
verrebbe impostato ad una stringa vuota mentre se non indicassimo il numero di reparto questo verrebbe impostato a 0 trattandosi di un intero.
Al contrario, verrebbe lanciato un errore se tentassimo di inventare una proprietà non definita nella struct. Se
provassimo ad istanziare tale elemento:
operaio2:=operaio{nome: "Pietro", matricola: "AX12345", cognome: "Arancioni" eta: 51}
avremmo un errore descritto da un messaggio in cui ci si segnalerebbe che la proprietà eta
non ha alcun senso e che
l'interprete si sarebbe aspettato una graffa chiusa o una virgola per proseguire correttamente la definizione del nuovo oggetto:
syntax error: unexpected eta, expecting comma or }
Esempio
Le struct possono essere considerate quindi una base per creare dei contenitori di dati non omogenei che nel loro
complesso costituiscono informazioni più complete. Proviamo i nostri due oggetti operaio
e confrontiamo i loro reparti per vedere se
sono colleghi dello stesso settore della fabbrica:
package main
import (
"fmt"
)
type operaio struct {
matricola string
nome string
cognome string
reparto int
}
func main() {
operaio1:=operaio{"AX12345", "Gigi", "Bianchi", 5}
operaio2:=operaio{nome: "Pietro", matricola: "AX12346",reparto: 1, cognome: "Arancioni"}
if operaio1.reparto==operaio2.reparto {
fmt.Println(operaio1.nome+" e "+operaio2.nome+" sono dello stesso reparto")
} else {
fmt.Println(operaio1.nome+" e "+operaio2.nome+" sono di reparti diversi")
}
}
Una volta definiti i due oggetti ne confrontiamo i reparti e facciamo stampare un messaggio in cui sarà specificato se sono
compagni di lavoro. In questo caso, si trovano uno al reparto 1 e l'altro al reparto 5 pertanto, sfruttando i
costrutti condizionali di Go, otterremo
il seguente risultato:
Gigi e Pietro sono di reparti diversi