2015-01-24 4 views
5

Supponiamo che io debba implementare due diverse interfacce dichiarate in due pacchetti diversi (in due diversi progetti separati).Come implementare due interfacce diverse con lo stesso metodo di firma

ho nel pacchetto A

package A 

type interface Doer { 
    Do() string 
} 

func FuncA(Doer doer) { 
    // Do some logic here using doer.Do() result 

    // The Doer interface that doer should implement, 
    // is the A.Doer 
} 

E nel pacchetto B

package B 

type interface Doer { 
    Do() string 
} 

function FuncB(Doer doer) { 
    // some logic using doer.Do() result 

    // The Doer interface that doer should implement, 
    // is the B.Doer 
} 

Nel mio pacchetto main

package main 

import (
    "path/to/A" 
    "path/to/B" 
) 

type C int 

// this method implement both A.Doer and B.Doer but 
// the implementation of Do here is the one required by A ! 
func (c C) Do() string { 
    return "C now Imppement both A and B" 
} 

func main() { 
    c := C(0) 
    A.FuncA(c) 
    B.FuncB(c) // the logic implemented by C.Do method will causes a bug here ! 
} 

Come affrontare questa situazione?

+2

Non c'è assolutamente nulla da trattare: ** Qualsiasi ** tipo che ha un metodo 'Do() stringa' implementa * entrambe * interfacce' A.Doer' e 'B.Doer'. – Volker

+0

Penso che tu abbia ragione @Volker, non c'è una soluzione per questo, e questa situazione può verificarsi in qualsiasi linguaggio che usi le interfacce ('java' per esempio). – tarrsalah

risposta

6

Come il FAQ mentions

L'esperienza con altre lingue ci ha detto che avere una varietà di metodi con lo stesso nome, ma firme diverse volte è stato utile, ma che potrebbe anche essere fonte di confusione e fragile nella pratica.
La corrispondenza solo per nome e che richiede coerenza nei tipi è stata una decisione semplificata importante nel sistema di tipo Go.

Nel tuo caso, soddisfi entrambe le interfacce.

È possibile la possibile verificare se un oggetto (di un tipo di interfaccia) soddisfa un altro tipo di interfaccia A.Doer, facendo:

if _, ok := obj.(A.Doer); ok { 
} 

Il PO aggiunge:

Ma la la logica implementata nel metodo Do per soddisfare A è completamente diversa da quella in B.

allora avete bisogno di implementare un wrapper obiettate:

  • un DoerA, che ha l'oggetto C come un campo, e implementare A.Do() in un modo che soddisfi come A.Do() dovrebbe funzionare
  • un DoerB, che ha il tuo stesso oggetto C come un campo, e implementare B.Do() in un modo che soddisfi come B.Do() dovrebbe funzionare

In questo modo, si saprà quale Doer passare a una funzione in attesa di A.Doer o B.Doer.
Non è necessario implementare un metodo Do() sull'oggetto originale C, che non sarebbe in grado di gestire la diversa logica di A.Do() e B.Do().

+0

ma la logica implementata nel metodo 'Do' per soddisfare' A 'è completamente diversa da quella in' B'. – tarrsalah

+0

@ tarrsalah Ho modificato la risposta. – VonC

+0

Grazie a @VonC, anche a me, ho aggiunto altro codice per chiarire il problema. – tarrsalah

3

Per definizione, you are satisfying both:

tipo A Go soddisfa un'interfaccia implementando i metodi di questa interfaccia, niente di più. Questa proprietà consente di definire e utilizzare le interfacce senza dover modificare il codice esistente. Abilita un tipo di tipizzazione strutturale che promuove la separazione delle preoccupazioni e migliora il riutilizzo del codice e facilita la costruzione di schemi che emergono man mano che il codice si sviluppa. La semantica delle interfacce è uno dei motivi principali per la sensazione agile e leggera di Go.

Così, con questo in mente, è possibile:

a) Aggiungere commenti ai metodi di interfaccia che definisce le vostre aspettative sulla logica (vedere l'interfaccia io.Reader o un buon esempio)

b) Aggiungere un metodo aggiuntivo chiamato ImplementsDoerA e ImplementsDoerB sulle interfacce (citato anche nelle FAQ).