Принцип обращения зависимостей против «Программы для интерфейса, а не для реализации»

12

Я пытаюсь понять, чем принцип инверсии зависимости отличается от принципа «программа - интерфейс, а не реализация».

Я понимаю, что означает «Программа для интерфейса, а не для реализации». Я также понимаю, как это позволяет создавать более гибкие и поддерживаемые конструкции.

Но я не понимаю, чем принцип инверсии зависимости отличается от принципа «Программа - интерфейс, а не реализация».

Я читал о DIP в нескольких местах в Интернете, и это не прояснило мою путаницу. Я до сих пор не понимаю, как эти два принципа отличаются друг от друга. Спасибо за вашу помощь.

Авив Кон
источник

Ответы:

25

«Программа для интерфейса» означает, что не зависит от конкретного типа для выполнения вашей работы , но он не определяет, как вы должны получить свою зависимость.

«Принцип инверсии зависимостей» говорит, что объект не должен контролировать создание своих зависимостей, он должен просто объявлять, какая зависимость ему нужна, и позволить вызывающей стороне предоставить ее . Но это не указывает, должна ли зависимость быть конкретным типом или интерфейсом.

Я проиллюстрирую различия с помощью некоторого кода на C #.

Следующий пример зависит от конкретного типа и управляет созданием собственной зависимости. Это не следует ни за «программой к интерфейсу», ни «инверсией зависимостей»:

public class ThingProcessor
{
    MyThing _myThing;

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

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

Следующий пример зависит от интерфейса, но он контролирует создание собственной зависимости. Это следует за «программой к интерфейсу», но не «инверсией зависимости»:

public class ThingProcessor
{
    IMyThing _myThing;

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

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

Следующий пример зависит от конкретного типа, но запрашивает, чтобы его зависимость была создана и передана ему. Это следует за "инверсией зависимости", но не "программой к интерфейсу":

public class ThingProcessor
{
    MyThing _myThing;

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

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

Следующий пример зависит от интерфейса и запрашивает, чтобы его зависимость была создана и передана ему. Это следует за "инверсией зависимости" и "программой к интерфейсу":

public class ThingProcessor
{
    IMyThing _myThing;

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

    public void DoSomething()
    {
        _myThing.DoIt();
    }
}
Эрик Кинг
источник
1
Отличная иллюстрация разницы.
Рори Хантер
8
То, о чем вы говорите, это зависимая инъекция. И инверсия зависимостей и внедрение зависимостей - две разные вещи.
Эйфорическая
1
@Euphoric Я говорю о принципе инверсии зависимостей, который является абстрактной концепцией, используя Dependency Injection в качестве конкретного примера реализации. Я понимаю разницу.
Эрик Кинг,
1
@EricKing Тогда вы должны прямо сказать, что в своем ответе вместо того, чтобы идти, «Принцип инверсии зависимостей» говорит… », что, очевидно, неправильно, если вы читаете мой ответ.
Эйфорическая
1
Я согласен с Euphoric. Принцип обращения зависимостей гласит, что уровни кода более высокого уровня должны зависеть от фрагментов кода более низкого уровня, а не наоборот. Например, PrintStreamдолжен зависеть от интерфейса, установленного ByteOutputStream. Внедрение зависимости ничего не говорит о том, кто должен зависеть от кого.
Доваль
5

Они вообще одно и то же. Если вы читаете, что такое принцип обращения зависимостей и почему он важен? и Принцип инверсии зависимости , вы поймете, что два «принципа» говорят в основном об одном и том же.

  • Модули высокого уровня не должны зависеть от модулей низкого уровня. Оба должны зависеть от абстракций.
  • Абстракции никогда не должны зависеть от деталей. Детали должны зависеть от абстракций.

Интерфейс - это абстракция, а реализация - это деталь. Если вы замените их в предыдущих двух утверждениях, вы получите «код должен зависеть от интерфейсов, а не от реализаций». И это звучит как то же самое для меня.

Euphoric
источник
Это должен быть принятый ответ, другой, получивший наибольшее количество голосов, вводит в заблуждение
Самех Дибес
2

Интерфейсы являются одним из способов реализации DI. Если вы указали интерфейс в качестве параметра в методе конструктора класса, вы можете передать любой понравившийся объект этому методу конструктора, если этот объект реализует интерфейс параметра конструктора.

Другими словами, программирование интерфейса позволяет изменить реализацию этого интерфейса. Это то, как мы можем заменить фиктивные объекты реальными объектами во время модульного тестирования, указать разных поставщиков данных и так далее.

Роберт Харви
источник