Zasada inwersji zależności a „Program do interfejsu, a nie implementacja”

12

Próbuję zrozumieć, w jaki sposób zasada inwersji zależności różni się od zasady „program do interfejsu, a nie implementacja”.

Rozumiem, co oznacza „Program do interfejsu, a nie implementacja”. Rozumiem również, w jaki sposób pozwala na bardziej elastyczne i łatwe w utrzymaniu projekty.

Ale nie rozumiem, w jaki sposób zasada inwersji zależności różni się od zasady „Program do interfejsu, a nie implementacja”.

Czytałem o DIP w kilku miejscach w sieci i nie wyjaśniło to mojego zamieszania. Nadal nie rozumiem, jak te dwie zasady różnią się od siebie. Dzięki za pomoc.

Aviv Cohn
źródło

Odpowiedzi:

26

„Program do interfejsu” oznacza, że nie zależy od konkretnego rodzaju pracy , ale nie określa, w jaki sposób należy uzyskać zależność.

„Zasada inwersji zależności” mówi, że obiekt nie powinien kontrolować tworzenia swoich zależności, powinien po prostu ogłosić, jakiej zależności potrzebuje i pozwolić, aby osoba dzwoniąca podała ją . Ale nie określa, czy zależność powinna być konkretnym typem, czy interfejsem.

Zilustruję różnice za pomocą kodu C #.

Poniższy przykład zależy od konkretnego typu i kontroluje tworzenie własnej zależności. Wynika ani „programu do interfejsu” ani „zależność inwersji”:

public class ThingProcessor
{
    MyThing _myThing;

    public ThingProcessor()
    {
        _myThing = new MyThing();
    }

    public void DoSomething()
    {
        _myThing.DoIt();
    }
}

Poniższy przykład zależy od interfejsu, ale kontroluje tworzenie własnej zależności. Wynika to z „program do interfejsu”, ale nie „inwersja zależności”:

public class ThingProcessor
{
    IMyThing _myThing;

    public ThingProcessor()
    {
        _myThing = ThingFactory.GiveMeANewMyThing();
    }

    public void DoSomething()
    {
        _myThing.DoIt();
    }
}

Poniższy przykład zależy od konkretnego typu, ale prosi o utworzenie i przekazanie zależności. Wynika to z „odwrócenia zależności”, ale nie „program do interfejsu”:

public class ThingProcessor
{
    MyThing _myThing;

    public ThingProcessor(MyThing myThing)
    {
        _myThing = myThing;
    }

    public void DoSomething()
    {
        _myThing.DoIt();
    }
}

Poniższy przykład zależy od interfejsu i prosi o utworzenie i przekazanie zależności. Wynika zarówno z „inwersji zależności”, jak i „programu do interfejsu”:

public class ThingProcessor
{
    IMyThing _myThing;

    public ThingProcessor(IMyThing myThing) // using an interface
    {
        _myThing = myThing;
    }

    public void DoSomething()
    {
        _myThing.DoIt();
    }
}
Eric King
źródło
1
Doskonała ilustracja różnicy.
Rory Hunter
8
Mówisz o zastrzyku zależnym. Odwrócenie zależności i wstrzyknięcie zależności to dwie różne rzeczy.
Euforii
1
@Euphoric Mówię o Zasadzie Inwersji Zależności, która jest abstrakcyjną koncepcją, wykorzystując Zastrzyk Zależności jako konkretny przykład implementacji. Rozumiem różnicę.
Eric King
1
@EricKing W takim razie powinieneś wyraźnie powiedzieć, że w swojej odpowiedzi zamiast iść „Zasada inwersji zależności” mówi… ”, co jest oczywiście błędne, jeśli przeczytasz moją odpowiedź.
Euforyczny
1
Zgadzam się z Euphoric. Zasada Inwersji Zależności mówi, że warstwy kodu wyższego poziomu powinny zależeć od fragmentów kodu niższego poziomu, a nie odwrotnie. Np. PrintStreamPowinien zależeć od interfejsu ustanowionego przez ByteOutputStream. Wstrzykiwanie zależności nie wspomina nic o tym, kto powinien zależeć od kogo.
Doval
5

Są ogólnie takie same. Jeśli przeczytasz Czym jest zasada inwersji zależności i dlaczego jest ważna? i Zasadę Inwersji Zależności , uświadomicie sobie, że dwie „zasady” mówią w zasadzie o tym samym.

  • Moduły wysokiego poziomu nie powinny zależeć od modułów niskiego poziomu. Oba powinny zależeć od abstrakcji.
  • Abstrakcje nigdy nie powinny zależeć od szczegółów. Szczegóły powinny zależeć od abstrakcji.

Interfejs to abstrakcja, a implementacja to szczegół. Jeśli podmienisz je w poprzednich dwóch instrukcjach, otrzymasz w zasadzie „kod powinien zależeć od interfejsów, a nie implementacji”. I to dla mnie brzmi tak samo.

Euforyk
źródło
To powinna być zaakceptowana odpowiedź, druga najczęściej głosowana odpowiedź jest myląca
Sameh Deabes
2

Interfejsy są jednym ze sposobów implementacji DI. Jeśli podasz interfejs jako parametr w metodzie konstruktora klasy, możesz przekazać dowolny obiekt tej metodzie konstruktora, o ile obiekt ten implementuje interfejs parametru konstruktora.

Innymi słowy, programowanie interfejsu pozwala zmienić implementację tego interfejsu. W ten sposób jesteśmy w stanie zastąpić fałszywe obiekty rzeczywistymi obiektami podczas testów jednostkowych, określić różnych dostawców danych i tak dalej.

Robert Harvey
źródło