依赖倒置原则

时间:2016-12-25 16:38:42

标签: java oop solid-principles dependency-inversion

我一直在阅读有关固体OOP原则(Dependency Inversion Principle)并且没有完全了解这是如何工作的。

  

当一个班级明确知道设计和实施时   另一个班级,改变一个班级提高了打破的风险   其他课程。

如果我改变课程对学生的影响,请说我有依赖课程的学生。使用DI是否相同我的意思是DI替换新的运算符,那么什么呢?学生仍然依赖于课程 你能举一些例子吗 谢谢

var app = angular.module('plunker', []);

app.controller('MainCtrl', function($scope) {
    $scope.newComment = 'asdf';
    $scope.existingItem = {};
    $scope.existingItem.comments = [];

 $scope.comment = function(existingItem, newComment){
   existingItem.comments.push(newComment);
   console.log(existingItem.comments);
      $scope.newComment = '';
    } 
});
  

更新1

(场景)如果我假设该类只有默认构造函数,它将永远不会使用任何实例变量来实例化public class Student { ..... private Course course = new Course(); }

  

更新2

实施例

new Course(name, .......)
  

现在发生的事情是我们的复制模块需要知道打印机和磁盘,您可以想象在这些情况下来拯救我们的那些神奇的if-else语句。随着新需求的出现,您可能会为此复制模块添加越来越多的依赖项。在一天结束时,你最终会得到一个非常复杂,难以维护和难以理解的设计。

你能否在这个例子中显示特定依赖倒置消除使用神奇if-else语句的简单例子

3 个答案:

答案 0 :(得分: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都是完整的。可以使用属性mutator或字段getter-setter方法完成相同的操作。

<强>编辑: 还有其他几种情况,您的Student课程需要更改。引入新Course类型OptionalMandatoryPrerequisite的要求。使用db或其他某些数据源创建Course实例。通过IDE等工具对代码进行重新分解。

答案 1 :(得分:2)

Prologue

依赖性倒置原则(DIP)首先在Bob Martin的一篇C ++报告中引入。在那篇论文中,他列举了以下五个原则:面向对象设计原理,又名 SOLID 原则:

  1. 单一责任原则(SRP)
  2. 开放封闭原则(OCP)
  3. Liskov替代原则(LSP)
  4. 接口隔离原则(ISP)
  5. 依赖性倒置原则(DIP)
  6. 您可以在The Principles of OOD文章中详细了解这些原则。

    定义

    鲍勃叔叔在论文中给出的正式定义是:

      

    高级模块不应该依赖于低级模块。都   应该取决于抽象。

         

    抽象不应该依赖   细节。细节应该取决于抽象。

    示例

    鲍勃叔叔在他的论文中给出的一个例子是复制程序,它从键盘读取并写入打印机:

    enter image description here

    这里的复制模块依赖于其他两个模块,到目前为止似乎是一个非常合理的设计。事实上,这两个使用过的模块可以很好地重复使用。需要从键盘读取或写入打印机的任何其他抽象都可能重用其提供的功能。

    但是,复制模块可以在任何不涉及键盘或打印机的 的上下文中重复使用(顺便说一下,这些是特定于实现的)。例如,假设我们有时不仅要写入打印机,而且还需要写入磁盘:

    enter image description here

    现在发生的事情是我们的复制模块需要了解打印机和磁盘,您可以想象在这些情况下拯救我们的那些神奇的if-else语句。随着新需求的出现,您可能会为此复制模块添加越来越多的依赖项。在一天结束时,你最终会得到一个非常复杂,难以维护和难以理解的设计。

    那么,我们糟糕的复制模块出了什么问题?问题不是取决于抽象,而是取决于读者和作者的具体实现。为了解决这个问题,复制模块应该依赖于 Reader Writer 抽象定义:

    enter image description here

    现在我们的复制模块依赖于其他两个抽象,我们可以通过将这些实现传递给复制模块,轻松地在不同的实现之间切换。

    进一步阅读

    原始论文链接现在似乎已经破解,但您可以阅读有关DIP here的更多信息。

答案 2 :(得分:0)

我会添加一个间接级别:clang -ffreestanding -m32 kernel_entry.o kernel.o $(OBJECTS) -o temp/kernel_temp.o gobjcopy -O binary temp/kernel_temp.o temp/kernel.bin 。课程协调课程的参与者,可能是单一的变革点。

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); } } } ProfessorStudent类。