Come implementare i concetti di programmazione orientata agli oggetti in Go
Affrontare la programmazione orientata agli oggetti in Go non è così difficile come pensi.
La programmazione orientata agli oggetti (OOP) è un paradigma di programmazione basato sugli oggetti come concetto centrale. In OOP, il codice viene formattato in base alla funzionalità, consentendo la manutenzione del codice, l'astrazione, la riusabilità, l'efficienza e numerose funzionalità sull'oggetto.
L'oggetto ha attributi (variabili) che ne definiscono caratteristiche, proprietà e metodi (funzioni) che definiscono le azioni (procedure) e i comportamenti dell'oggetto.
La programmazione orientata agli oggetti in Go è diversa dagli altri linguaggi. I concetti orientati agli oggetti vengono implementati in Go utilizzando strutture, interfacce e tipi personalizzati.
Personalizzazione dei tipi in Go
I tipi personalizzati semplificano il raggruppamento e l'identificazione di codici simili per il riutilizzo.
Il codice per dichiarare i tipi personalizzati è:
type typeName dataType
Quando crei un tipo personalizzato e assegni una variabile, puoi controllare il tipo utilizzando reflect.TypeOf() che accetta una variabile e restituisce il tipo della variabile.
import( "fmt"
"reflect")
type two int // creates type "two"
var number two // variable of type "two"
fmt.Println(reflect.TypeOf(number))
La variabile numero è un tipo di due che è un numero intero. Puoi andare oltre per creare più tipi personalizzati.
Creazione di strutture in Go
Le strutture (strutture) sono i modelli per la programmazione orientata agli oggetti in Go. Le strutture sono raccolte di campi definite dall'utente.
Una struttura può contenere una varietà di tipi di dati, inclusi tipi e metodi composti.
Puoi creare una struttura usando questa sintassi:
type StructName struct {
// some code
}
Convenzionalmente, i nomi delle strutture sono solitamente in maiuscolo e in lettere di cammello per facilitarne la leggibilità.
Il tipo di struttura accetta nomi di campo e tipi di dati. Le strutture possono accettare qualsiasi tipo di dati Go, inclusi i tipi personalizzati.
type User struct {
field1 string
field2 int
fieldMap map[string]int
}
È possibile creare un'istanza di un tipo di struttura assegnando la struttura come variabile.
instance := User{
// some code
}
L'istanza della struttura può essere popolata con campi durante la creazione di istanze come definito durante l'inizializzazione o impostata su null.
instance := User{
field1: "a string field",
field2: 10,
fieldMap: map[string]int{},
}
Accesso agli elementi della struttura
È possibile accedere ai campi di un'istanza di struttura utilizzando una notazione punto nel campo.
fmt.Println("Accessing a field of value", instance.field2)
Questo restituisce il field2 dell'istanza della struttura istanziata.
Assegnazione di metodi alle strutture
Le funzioni (metodi) vengono assegnate ai tipi di struttura specificando un nome di destinatario e il nome di struttura prima del nome di funzione come mostrato nella sintassi seguente.
func (receiver StructName) functionName() {
// some code
}
Il metodo functionName può essere utilizzato solo sul tipo di struttura specificato.
Implementazione dell'ereditarietà in Go
L'ereditarietà è la capacità degli oggetti e dei tipi di accedere e utilizzare metodi e attributi di altri oggetti. Go non ha l'ereditarietà come funzionalità, ma puoi utilizzare le composizioni. In Go, la composizione implica fare riferimento a una superstruttura (la struttura da ereditare) in una sottostruttura fornendo il nome della superstruttura alla sottostruttura.
Utilizzando l'esempio di struttura sopra:
type User struct {
field1 string
field2 int
fieldMap map[string]int
}
type User2 struct {
User
}
Passando il nome della struttura User nella struttura User2, la struttura User2 può accedere a tutti i metodi e gli attributi della struttura User struct sull'istanziazione, tranne per il fatto che vengono impiegate tecniche di astrazione.
son := User2{
User{
field1: "baby",
field2: 0,
fieldMap: nil,
},
}
fmt.Println(son.field2)
La variabile son sopra è un'istanza della struttura User2. Come visto nell'esempio, la variabile son può accedere e istanziare valori di tipo Utente e utilizzarli.
Incapsulamento dei campi di tipo in Go
L'incapsulamento, noto anche come "occultamento delle informazioni", è una tecnica che consente di raggruppare i metodi e gli attributi di un oggetto in unità per limitarne l'uso e l'accesso tranne quelli specificati (abilitando i privilegi di lettura/scrittura).
L'incapsulamento è implementato in Go utilizzando identificatori esportati e non esportati nei pacchetti.
Identificatori esportati (lettura e scrittura)
Gli identificatori esportati vengono esportati dai pacchetti definiti e accedono ad altri programmi. L'utilizzo delle maiuscole per un identificatore di campo esporta il campo fo.
type User struct {
Field1 string
Field2 int
FieldMap map[string]int
}
type User2 struct {
User
}
Identificatori non esportati (sola lettura)
Gli identificatori non esportati non vengono esportati dal pacchetto definito e sono convenzionalmente scritti in minuscolo.
type User struct {
field1 string
field2 int
fieldMap map[string]int
}
type User2 struct {
User
}
Il concetto di identificatori esportati e non esportati si applica anche ai metodi di un oggetto.
Polimorfismo in Go
Il polimorfismo è una tecnica utilizzata per dare forme diverse a un oggetto per flessibilità.
Go implementa il polimorfismo utilizzando le interfacce. Le interfacce sono tipi personalizzati utilizzati per definire le firme dei metodi.
Dichiarazione delle interfacce
La dichiarazione delle interfacce è simile alla dichiarazione delle strutture. Tuttavia, le interfacce vengono dichiarate utilizzando la parola chiave interface.
type InterfaceName interface{
//some methods
}
Le dichiarazioni di interfaccia contengono metodi che devono essere implementati dai tipi struct.
Implementazione di interfacce nelle strutture
È necessario dichiarare i tipi che implementano l'interfaccia, dopodiché i metodi del tipo implementano l'interfaccia.
// The Interface
type Color interface{
Paint() string
}
// Declaring the structs
type Green struct {
// some struct specific code
}
type Blue struct {
// some specific code
}
Lo snippet di codice sopra ha un'interfaccia Color dichiarata con un metodo Paint che deve essere implementato dalla struttura Green e Blue tipi.
Le interfacce vengono implementate assegnando metodi ai tipi di struttura e quindi denominando il metodo in base al metodo dell'interfaccia da implementare.
func (g Green) Paint() string {
return "painted green"
}
func (b Blue) Paint() string {
return "painted blue"
}
Il metodo Paint è implementato dai tipi Green e Blue che ora possono chiamare e utilizzare il metodo Paint.
brush := Green{}
fmt.Println(brush.Paint())
Sulla console viene stampato "Dipinto di verde" a conferma che l'interfaccia è stata implementata con successo.
Astrazione dei campi in Go
L'astrazione è il processo che nasconde metodi e attributi non importanti di un tipo, rendendo più semplice proteggere parti del programma da un uso anomalo e non intenzionale.
Go non ha l'astrazione implementata immediatamente; tuttavia, puoi procedere attraverso l'implementazione dell'astrazione utilizzando le interfacce.
// humans can run
type Human interface {
run() string
}
// Boy is a human with legs
type Boy struct {
Legs string
}
// a method on boy implements the run method of the Human interface
func (h Boy) run() string {
return h.Legs
}
Il codice sopra crea un'interfaccia Human con un'interfaccia run che restituisce una stringa. Il tipo Boy implementa il metodo run dell'interfaccia Human e restituisce una stringa durante l'istanziazione.
Uno dei modi per implementare l'astrazione è fare in modo che una struttura erediti l'interfaccia i cui metodi devono essere astratti. Esistono molti altri approcci, ma questo è il più semplice.
type Person struct {
Name string
Age int
Status Human
}
func main() {
person1 := &Boy{Legs: "two legs"}
person2 := &Person{ // instance of a person
Name: "amina",
Age: 19,
Status: person1,
}
fmt.Println(person.Status.run())
}
La struttura Person eredita l'interfaccia Human e può accedere a tutti i suoi metodi utilizzando la variabile Status che eredita l'interfaccia.
Nell'istanziazione per riferimento (utilizzando un puntatore), l'istanza della struttura Person Person2 fa riferimento a un'istanza della struttura Boy Person1< e ottiene l'accesso ai metodi.
In questo modo, puoi specificare metodi specifici da implementare in base al tipo.
OOP vs Programmazione Funzionale
La programmazione orientata agli oggetti è un paradigma importante in quanto offre un maggiore controllo sul programma e incoraggia il riutilizzo del codice in modi in cui la programmazione funzionale non lo fa.
Ciò non rende la programmazione funzionale una cattiva scelta, poiché la programmazione funzionale può essere utile e migliore per alcuni casi d’uso.