0

我一直在阅读可靠的 OOP 原则(依赖倒置原则),但并不太了解它是如何工作的。

当一个类明确知道另一个类的设计和实现时,对一个类的更改会增加破坏另一个类的风险。

假设我有一个取决于课程的学生,如果我会改变课程对学生的影响。使用 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 语句的简单例子吗?

4

3 回答 3

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类型OptionalMandatory或的要求PrerequisiteCourse使用来自 db 或其他数据源的值创建实例。通过 IDE 等工具重构代码。

于 2016-12-25T16:58:11.443 回答
2

序幕

依赖倒置原则 (DIP)最早是在 Bob Martin 的一篇 C++ 报告中的论文中引入的。在那篇论文中,他列举了以下五个原则作为面向对象设计的原则,即SOLID原则:

  1. 单一职责原则 (SRP)
  2. 开闭原则 (OCP)
  3. 里氏替换原则 (LSP)
  4. 接口隔离原则 (ISP)
  5. 依赖倒置原则(DIP)

您可以在OOD 原则一文中了解有关这些原则的更多信息。

定义

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

高级模块不应该依赖于低级模块。两者都应该依赖于抽象。

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

例子

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

在此处输入图像描述

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

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

在此处输入图像描述

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

那么,我们糟糕的复制模块出了什么问题呢?问题不是依赖于Abstractions,而是依赖于reader 和 writers 的具体实现。为了解决这个问题,复制模块应该依赖于ReaderWriter的抽象定义:

在此处输入图像描述

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

延伸阅读

原始论文链接现在似乎已损坏,但您可以在此处阅读有关 DIP的更多信息。

于 2016-12-25T18:09:26.360 回答
0

我会添加一个间接级别: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类。Staff

于 2016-12-25T20:03:22.177 回答