Принцип инверсии зависимостей

Я читал о твердых принципах ООП (принцип инверсии зависимости) и не совсем понял, как это работает.

Когда один класс явно знает о дизайне и реализации другого класса, изменения в одном классе повышают риск нарушения другого класса.

скажем, у меня есть студент, который зависит от курса, если я изменю курс, как он повлияет на студента. это то же самое, что использовать DI Я имею в виду DI заменить новый оператор, так что же тогда? студент по-прежнему зависит от курса
Можете ли вы привести несколько примеров, пожалуйста.
Спасибо

public class Student {
.....
private Course course = new Course(); 
}

обновлено 1

(сценарий), если я буду предполагать, что класс имеет только конструктор по умолчанию, и он никогда не будет использовать какие-либо переменные экземпляра для создания экземпляров, как new Course(name, .......)

обновлено 2

пример

public class Copy {
    @Autowired
    private Writer writer;
.....
}

public interface Writer{
    void write();
}


public class PrinterWriter implements Writer {
.....
}

public class DiskWriter implements Writer {
....
}

Теперь, когда происходит то, что нашему копирующему модулю нужно знать о принтере и диске, вы можете представить себе те магические операторы if-else, которые приходят, чтобы спасти нас в этих ситуациях. По мере появления новых требований вы, вероятно, добавляете все больше и больше зависимостей в этот модуль копирования. В конце концов, вы получите очень сложный, сложный в обслуживании и сложный для понимания дизайн.

Можете ли вы показать в этом примере, где конкретно инверсия зависимостей исключает использование магических операторов if-else с простым примером, пожалуйста

3 ответа

Возьмите сценарий, где, наверняка, Course должен изменить конструктор. Допустим, добавляется новый параметр, и по умолчанию в конструкторе нет Course учебный класс.

public class Course{
  public Course(String name){}
}

Сейчас Student класс будет иметь ошибку компилятора.

public class Student {
   private Course course = new Course(); //ERROR !! no such constructor exist 
}

С DI это как Student класс может быть реализован с помощью конструктора:

public class Student {
   private Course course;
   public Student(Course course){
      this.course=course;
   }
}

Так что здесь, независимо от изменений в Course, учебный класс Student не поврежден То же самое можно сделать, используя мутаторы свойств или методы getter-setter.

Отредактировано: Есть несколько других случаев, когда ваш Student класс потребует изменений. Требование для введения нового Course типы Optional а также Mandatory или же Prerequisite, Создание экземпляра Course со значениями из БД или другого источника данных. Рефакторинг кода такими инструментами, как IDE.

пролог

Принцип обращения зависимостей (DIP) был впервые представлен в одной из работ Боба Мартина в отчете C++. В этой статье он перечислил пять следующих принципов как "Принципы объектно-ориентированного проектирования", или "Принципы SOLID":

  1. Принцип единой ответственности (SRP)
  2. Открытый Закрытый Принцип (OCP)
  3. Принцип замещения Лискова (LSP)
  4. Принцип разделения интерфейса (ISP)
  5. Принцип обращения зависимостей (DIP)

Вы можете прочитать больше об этих принципах в статье Принципы OOD.

Определение

Формальное определение, которое дядя Боб дал в статье:

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

Абстракции не должны зависеть от деталей. Детали должны зависеть от абстракций.

пример

Одним из примеров, которые дядя Боб привел в своей статье, была Программа копирования, которая читает с клавиатуры и пишет на принтер:

введите описание изображения здесь

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

Тем не менее, модуль копирования нельзя использовать повторно ни в каком контексте , в котором не задействованы клавиатура или принтер (кстати, они очень специфичны для реализации). Например, предположим, что вместо простой записи на принтер нам иногда нужно записать и на диск:

введите описание изображения здесь

Теперь происходит то, что наш модуль копирования должен знать о принтере и диске, вы можете себе представить if-else заявления, которые приходят, чтобы спасти нас в этих ситуациях. По мере появления новых требований вы, вероятно, добавляете все больше и больше зависимостей в этот модуль копирования. В конце концов, вы получите очень сложный, сложный в обслуживании и сложный для понимания дизайн.

Итак, что не так с нашим плохим модулем копирования? Проблема была не в зависимости от абстракций, а в конкретных реализациях читателей и писателей. Чтобы решить эту проблему, модуль копирования должен зависеть от абстрактных определений Reader и Writer:

введите описание изображения здесь

Теперь, когда наш модуль копирования зависит от двух других абстракций, мы можем легко переключаться между различными реализациями, передавая эти реализации в модуль копирования.

Дальнейшее чтение

Оригинальная ссылка на документ сейчас не работает, но вы можете узнать больше о DIP здесь.

Я бы добавил уровень косвенности: class Curriculum, Класс координирует участников курса и может быть единственной точкой изменения.

interface CourseSystem {

  enum Role { PROFESSOR, STUDENT, STAFF };

  /**
   * Abstraction over a course. It does not know
   */
  interface Course {
    boolean addParticipant(Participant participant)
  }

  interface Curriculum {
    boolean register(Participant participant);
  }

  interface Participant {
    default void setCurriculum(final Curriculum curriculum) {
      curriculum.register(this);
    }
  }
}

Отсюда вы можете реализовать бетон Professor, Student или же Staff классы, которые имеют специальные привилегии и т. д.

Другие вопросы по тегам