Jaka jest motywacja adnotacji @ImplementedBy w Guice?


10

Niedawno przeczytałem o @ImplementedByadnotacjach dostępnych w Google Guice . Pozwala programiście określić powiązanie między interfejsem a jego implementacją do wykorzystania w przyszłości we wstrzykiwaniu zależności. Jest to przykład wiązania just-in-time .

Jestem przyzwyczajony do definiowania jawnych powiązań w moich modułach, używając następującej składni:

bind(SomeInterface.class).to(SomeInterfaceImplementation.class);

Zgodnie z dokumentacją jest to równoznaczne z następującym użyciem @ImplementedByadnotacji:

@ImplementedBy(SomeInterfaceImplementation.class)
public interface SomeInterface {
    //method declarations
}

Jedyne korzyści, jakie widzę tutaj, to to, że kod jest nieznacznie krótszy. Jednocześnie to podejście ma wadę słusznie wskazaną przez te same dokumenty:

Używaj @ImplementedByostrożnie; dodaje zależność czasu kompilacji od interfejsu do jego implementacji.

Taka zależność może nie być problemem w wielu przypadkach, ale osobiście postrzegam ją jako zapach kodu.

Z jakich przypadków użycia @ImplementedBywarto skorzystać z adnotacji?

Jednym z możliwych sposobów wydaje się zastosowanie go w kodzie biblioteki lub frameworka. Jak opisano w dokumentach, adnotacja może zapewnić domyślne powiązanie, które można łatwo zastąpić jawnym.

Jeśli typ znajduje się zarówno w bind()instrukcji (jako pierwszy argument) i ma @ImplementedByadnotację, bind()używana jest instrukcja. Adnotacja sugeruje domyślną implementację, którą można zastąpić powiązaniem.

W ten sposób, jako twórca biblioteki, mogę zapewnić moim użytkownikom niestandardowe powiązanie, które można dostosować gdzieś w kodzie klienta.

Czy to jedyny powód istnienia adnotacji? A może brakuje mi czegoś? Czy mogę coś zyskać, używając go w kodzie, który jest tylko aplikacją zajmującą się logiką biznesową, a nie biblioteką / strukturą do rozszerzenia?


2
Powiązane, być może zduplikowane pytanie (chociaż twój tytuł jest jaśniejszy): Czy Guice jest @ImplementedBy zły?
Jeff Bowman

Nie jest to ścisły duplikat, ale odbyła się tutaj interesująca dyskusja: stackoverflow.com/questions/6197178/...
Richard Vodden

Odpowiedzi:


8

Myślę, że tu jest niebezpieczeństwo przy użyciu wyłącznie z @ImplementedByadnotacji. Stosowane odpowiednio, w połączeniu z bind()instrukcjami w module itd., Jest w porządku.

Posiadanie domyślnej implementacji doskonale nadaje się do testowania; niekoniecznie chcesz jawnie definiować próbny zastrzyk za każdym razem, gdy testujesz klasę, która ma wiele zależności, lub jeśli masz klasę, od której zależy wiele rzeczy (więc musisz definiować próbkę za każdym razem ).

Na przykład możesz mieć klasę:

@ImplementedBy(NoOpDataService.class)
interface DataService {
    Map<String, MyPOJO> getData();
}

A potem NoOpDataServicejest:

class NoOpDataService implements DataService {
    @Override
    public Map<String, MyPOJO> getData() {
        return Collections.emptyMap();
    }
}

Oczywiście nigdy nie wykorzystasz tego w swoim kodzie; w module Guice powiązałbyś implementację, która faktycznie coś robi. Ale wszystkie testy na klasach, które zostały wstrzyknięte DataService, nie muszą już mieć próbnego wiązania.

tl; dr Zgadzam się z tobą, że posiadanie interfejsów zależy od twojej implementacji może być zapachem kodu; ale może również usunąć kod płyty głównej, aby ułatwić testowanie. Funkcja nie jest trudna do wdrożenia; i chociaż istnieje niewielki potencjał do nadużyć, ostatecznie konsekwencje nie mogą być tak złe (usługa zaczyna się z zaskoczenia) i nie byłoby to trudne do naprawienia, nawet gdyby tak się stało.


3
Dodajesz kod testowy do produkcji?
Basilevs,
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.