2015-01-24 8 views
5

Załóżmy, że muszę zaimplementować dwa różne interfejsy zadeklarowane w dwóch różnych pakietach (w dwóch różnych oddzielnych projektach).Jak zaimplementować dwa różne interfejsy o tym samym metodzie podpisu

mam w pakiecie 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 
} 

aw pakiecie 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 
} 

W moim main pakietu

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 ! 
} 

Jak radzić sobie z tą sytuacją?

+2

Nie ma absolutnie nic do czynienia z: ** Dowolny ** typ, który ma metodę 'Do() string' implementuje * oba * interfejsy' A.Doer' i 'B.Doer'. – Volker

+0

Myślę, że masz rację: @Volker, nie ma na to poprawki, a ta sytuacja może wystąpić w każdym języku, który używa interfejsów (na przykład 'java'). – tarrsalah

Odpowiedz

6

Jako FAQ mentions

Doświadczenie z innych języków powiedział nam, że o wiele metod o tej samej nazwie, ale różnych podpisów było czasami przydatne, ale że może to być mylące i kruchy w praktyce.
Dopasowywanie tylko według nazwy i wymaganie spójności w typach było główną decyzją upraszczającą w systemie typu Go o nazwie.

W twoim przypadku możesz zadowolić oba interfejsy.

Można można testu czy obiekt (od typu interfejsu) spełnia inny typ interfejsu A.Doer, wykonując:

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

PO dodaje:

ale Logika zaimplementowana w metodzie Do w celu zaspokojenia A jest całkowicie odmienna od tej w B.

Następnie trzeba wdrożyć otoki wokół siebie obiektów:

  • DoerA, który ma swój obiekt C jako pole, i wdrożenie A.Do() w taki sposób, aby zadowolić jak A.Do() ma pracować
  • a DoerB, który ma ten sam obiekt C jako pole, i zaimplementuj B.Do() w sposób, który spełnia warunki, jak B.Do() ma działać

W ten sposób dowiesz się, który Doer przejdzie do funkcji oczekującej na A.Doer lub B.Doer.
Nie musisz wdrażać metody Do() na oryginalnym obiekcie C, który nie byłby w stanie poradzić sobie z inną logiką: A.Do() i B.Do().

+0

ale logika zaimplementowana w metodzie "Do" w celu zaspokojenia 'A' jest całkowicie odmienna od tej w 'B'. – tarrsalah

+0

@tarrsalah Edytowałem odpowiedź. – VonC

+0

Dzięki @VonC, ja też dodałem więcej kodu, aby wyjaśnić problem. – tarrsalah

3

Z definicji you are satisfying both:

typ A Go spełnia interfejs poprzez wdrożenie metod tego interfejsu, nic więcej. Ta właściwość umożliwia definiowanie i używanie interfejsów bez konieczności modyfikowania istniejącego kodu. Pozwala na rodzaj strukturalnego pisania, który promuje rozdzielanie problemów i poprawia ponowne użycie kodu, a także ułatwia budowanie na wzorach, które pojawiają się wraz z rozwojem kodu. Semantyka interfejsów jest jednym z głównych powodów, dla których Go jest zwinny i lekki.

więc biorąc to pod uwagę, można:

a) Dodawanie komentarzy do metody interfejsu definiowania oczekiwania na logice (patrz interfejs io.Reader lub dobry przykład)

b) Dodaj dodatkową metodę o nazwie ImplementsDoerA i ImplementsDoerB na interfejsie (również wspomniano w FAQ).