Принцип инверсии зависимостей
Я читал о твердых принципах ООП (принцип инверсии зависимости) и не совсем понял, как это работает.
Когда один класс явно знает о дизайне и реализации другого класса, изменения в одном классе повышают риск нарушения другого класса.
скажем, у меня есть студент, который зависит от курса, если я изменю курс, как он повлияет на студента. это то же самое, что использовать 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":
- Принцип единой ответственности (SRP)
- Открытый Закрытый Принцип (OCP)
- Принцип замещения Лискова (LSP)
- Принцип разделения интерфейса (ISP)
- Принцип обращения зависимостей (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
классы, которые имеют специальные привилегии и т. д.