Quando Rob Pike dice "Go è sulla composizione", cosa significa esattamente? [chiuso]


Risposte:


13

Vuol dire che dove useresti qualcosa nell'ordine di:

class A : public B {};

in qualcosa come Java o C ++, in Go useresti (qualcosa di equivalente a):

class A {
    B b;
};

Sì, questo fornisce funzionalità simili all'ereditarietà. Espandiamo un po 'l'esempio sopra:

struct B {
    int foo() {}
};

struct A { 
    B b;
};

A a;

a.foo();  // not allowed in C++ or Java, but allowed in Go.

Per fare ciò, tuttavia, si utilizza una sintassi non consentita in C ++ o Java: si lascia l'oggetto incorporato senza un nome proprio, quindi è più simile a:

struct A {
   B;
};

1
Sono curioso, lo faccio in C ++ (preferisco la composizione). Go offre funzionalità che mi aiutano a comporre quando in Java / C ++ dovrei ereditare?
Doug T.

2
@DougT .: Sì, ho modificato in un esempio che mostra l'idea generale di (parte di) ciò che consente.
Jerry Coffin,

2
Penso che questo non vada a buon fine: la differenza non è solo sintattica e implica che usi l'incorporamento per costruire la tua tassonomia. Il fatto è che la mancanza di override del metodo OOP ti impedisce di costruire la tua tassonomia classica e devi invece usare la composizione.
Denys Séguret,

1
@dystroy: rispetto a Java, probabilmente hai ragione. Rispetto al C ++, non tanto - perché (almeno tra quelli con un indizio) quelle gigantesche tassonomie sono state viste l'ultima volta circa 20 anni fa.
Jerry Coffin,

1
@dystroy: non stai ancora capendo. Una gerarchia a tre livelli nel C ++ moderno è quasi sconosciuta. In C ++ vedrai quelli nella libreria iostreams e nella gerarchia delle eccezioni, ma vicini a nessun altro. Se la biblioteca di iostreams fosse stata progettata oggi, penso che sia sicuro dire che non sarebbe neanche così. In conclusione: i tuoi argomenti mostrano meno su C ++ che su quanto sei fuori contatto con esso. Dato che non lo usi da decenni, ha senso. Ciò che non ha senso è cercare di dire come viene usato il C ++ in base a quell'esperienza datata.
Jerry Coffin,

8

Questa domanda / problema è simile a questa .

In Go, non hai davvero OOP.

Se vuoi "specializzare" un oggetto, lo fai incorporando, che è una composizione, ma con alcune chicche che lo rendono parzialmente simile all'eredità. Lo fai in questo modo:

type ConnexionMysql struct {
    *sql.DB
}

In questo esempio, ConnexionMysql è un tipo di specializzazione di * sql.DB e puoi chiamare ConnexionMysql le funzioni definite su * sql.DB:

type BaseMysql struct {
    user     string
    password string
    database string
}

func (store *BaseMysql) DB() (ConnexionMysql, error) {
    db, err := sql.Open("mymysql", store.database+"/"+store.user+"/"+store.password)
    return ConnexionMysql{db}, err
}

func (con ConnexionMysql) EtatBraldun(idBraldun uint) (*EtatBraldun, error) {
    row := con.QueryRow("select pv, pvmax, pa, tour, dla, faim from compte where id=?", idBraldun)
    // stuff
    return nil, err
}

// somewhere else:
con, err := ms.bd.DB()
defer con.Close()
// ...
somethings, err = con.EtatBraldun(id)

Quindi a prima vista potresti pensare che questa composizione sia lo strumento per rendere la tua solita tassonomia.

Ma

se una funzione definita su * sql.DB chiama altre funzioni definite su * sql.DB, non chiamerà le funzioni ridefinite su ConnexionMysql anche se esistono.

Con l'eredità classica, fai spesso qualcosa del genere:

func (db *sql.DB) doComplexThing() {
   db.doSimpleThing()
   db.doAnotherSimpleThing()
}

func (db *sql.DB) doSimpleThing() {
   // standard implementation, that we expect to override
}

Cioè, definisci doComplexThingla super classe come un'organizzazione su chiamate delle specializzazioni.

Ma in Go, questo non chiamerebbe la funzione specializzata ma la funzione "superclasse".

Quindi, se vuoi avere un algoritmo che ha bisogno di chiamare alcune funzioni definite su * sql.DB ma ridefinite su ConnexionMySQL (o altre specializzazioni), non puoi definire questo algoritmo come una funzione di * sql.DB ma devi definirlo altrove e questa funzione comporrà solo le chiamate alla specializzazione fornita.

Puoi farlo in questo modo usando le interfacce:

type interface SimpleThingDoer {
   doSimpleThing()
   doAnotherSimpleThing()
}

func doComplexThing(db SimpleThingDoer) {
   db.doSimpleThing()
   db.doAnotherSimpleThing()
}

func (db *sql.DB) doSimpleThing() {
   // standard implementation, that we expect to override
}

func (db ConnexionMySQL) doSimpleThing() {
   // other implemenation
}

Questo è abbastanza diverso dalla classica sostituzione delle gerarchie di classi.

In particolare, ovviamente non è possibile avere direttamente un terzo livello che eredita un'implementazione della funzione dal secondo.

In pratica, finirai per usare principalmente interfacce (ortogonali) e lasciare che la funzione componga le chiamate su un'implementazione fornita invece di avere la "superclasse" dell'implementazione che organizza tali chiamate.

Nella mia esperienza, ciò porta all'assenza pratica di gerarchie più profonde di un livello.

Troppo spesso, in altre lingue, hai il riflesso, quando vedi che il concetto A è una specializzazione del concetto B, per reimpostare questo fatto creando una classe B e una classe A come sottoclasse di B. Invece di creare il tuo programma attorno ai tuoi dati, passi il tempo a riprodurre la tassonomia degli oggetti nel tuo codice, sul principio che questa è realtà.

In Go non puoi definire un algoritmo generale e specializzarlo. È necessario definire un algoritmo generale e assicurarsi che sia generale e funzioni con le implementazioni dell'interfaccia fornite.

Essendo stato inorridito dalla crescente complessità di alcuni alberi della gerarchia su cui i programmatori stavano realizzando hack complessi per provare ad accogliere un algoritmo la cui logica implica infine tutti i livelli, direi che sono felice con la logica Go più semplice, anche se forza a pensare invece di semplicemente reificare i concetti del modello di applicazione.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.