Quando Rob Pike dice "Go is about composition", cosa intende esattamente? [chiuso]

12

Da Meno è esponenzialmente Altro

If C++ and Java are about type hierarchies and the taxonomy of types, Go is about composition.

    
posta CMinus 18.07.2012 - 16:34
fonte

2 risposte

13

Vuol dire che dovresti usare qualcosa nell'ordine di:

class A : public B {};

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

class A {
    B b;
};

Sì, questo fornisce funzionalità simili all'eredità. 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 proprio nome, quindi è più simile a:

struct A {
   B;
};
    
risposta data 18.07.2012 - 17:05
fonte
8

Questa domanda / problema è simile a questo .

In Go, non hai davvero OOP.

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

type ConnexionMysql struct {
    *sql.DB
}

In questo esempio, ConnexionMysql è un tipo di specializzazione di * sql.DB, e puoi chiamare su 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 normale 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'ereditarietà classica, spesso fai 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 doComplexThing sulla super classe come organizzazione sulle chiamate delle specializzazioni.

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

Quindi, se si desidera che un algoritmo debba chiamare alcune funzioni definite su * sql.DB ma ridefinite su ConnexionMySQL (o altre specializzazioni), non è possibile definire questo algoritmo come funzione di * sql.DB, ma deve definiscilo altrove e questa funzione comporrà solo le chiamate alla specializzazione fornita.

Potresti 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 è molto diverso dal classico override delle gerarchie di classi.

Soprattutto, ovviamente non è possibile avere direttamente un terzo livello ereditando un'implementazione di funzione dalla seconda.

In pratica, finirai per utilizzare principalmente le interfacce (ortogonali) e consenti alla funzione di comporre le chiamate su un'implementazione fornita invece di avere la "superclasse" dell'implementazione che organizza quelle chiamate.

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

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

In Go non è possibile definire un algoritmo generale e specializzarlo. Devi definire un algoritmo generale e assicurarti che sia generale e funzioni con le implementazioni dell'interfaccia fornite.

Essendo stato inorridito dalla crescente complessità di alcuni alberi gerarchici su cui i codificatori stavano facendo complessi hack per cercare di accogliere un algoritmo la cui logica alla fine implica tutti i livelli, direi che sono felice con la logica Go più semplice, anche se ti costringe a pensare invece di reificare i concetti del tuo modello di applicazione.

    
risposta data 18.07.2012 - 17:10
fonte

Leggi altre domande sui tag