1

我正在自学 SOLID 面向对象编程中涉及的原则,并且无法理解字母 D 中的所有细节(依赖倒置原则。)

我正在阅读它在 Wikipedia ( http://en.wikipedia.org/wiki/Dependency_inversion_principle ) 中的条目,但我不理解图中的所有内容:

http://en.wikipedia.org/wiki/Dependency_inversion_principle#/media/File:DIPLayersPattern_v2.png

我注意到有两种不同类型的箭头——一种是虚线的,一种是实心的。

根据我目前的理解,虚线代表Java中“implements”关键字的等价物,实线代表关键字“extends”。

这是正确的解释吗?

4

2 回答 2

3

这可能会澄清一些事情:

UML 箭头的解释

请注意,这些图像适用于 Visual Studio,但我认为大多数信息与大多数 UML 文档是等效的。

虚线表示“实现”的等价物

UML 中实体的命名也让我有些吃惊......它似乎Policy依赖于一个接口,该接口描述了对较低级别模块的合同Mechanism

然而,在第二条(实线)应该代表继承(至少我相信)。 Mechanism实现接口(抽象)Policy而不是在Policy引用具体时应用 DIP 之前Mechanism

它试图传达的主要是类不应该依赖于其他类,但是它们可以依赖于抽象(接口)而不是具体的。

最简单的例子:原始 Foo/Logger 依赖于较低级别的模块。

// "Low level Module" Mechanism equivilant
public class Logger {
    public void logInformation(String logInfo) {
        System.out.println(logInfo);
    }
}

// "High level module" Policy equivalent.
public class Foo {
    // direct dependency of a low level module.
    private Logger logger = new Logger();

    public void doStuff() {
        logger.logInformation("Something important.");
    }
}

在上面,Foo取决于具体的实现Logger。这可以这样重构(注意有几种方法可以做到这一点,这只是一种)

public interface ILogger {
    void logInformation(String logInfo);
}

public class Logger implements ILogger {
    @Override
    public void logInformation(string logInfo) {
        System.out.println(logInfo);
    }
}

public class Foo {
    private ILogger logger;
    public void setLoggerImpl(ILogger loggerImpl) {
        this.logger = loggerImpl;
    }

    public void doStuff() {
        logger.logInformation("Something important.");
    }
}

在这个重构中,Foo不再依赖于Logger,而是现在利用了接口ILogger——这意味着您可以在运行时切换 ILogger 的实现、对象实例化等。

你可以这样消费Foo

Foo foo = new Foo();
ILogger logger = new Logger();
foo.setLoggerImpl(logger);
foo.doStuff();

这当然会打印到控制台“重要的事情”。现在,如果您不想登录到控制台,而是登录到数据库,会发生什么?

public class LoggerToDb implements ILogger {
    @Override
    public void logInformation(string logInfo) {
        DbContext databaseContext = new DbContext();
        databaseContext.insertLog(logInfo);
    }
}

现在可以消费为:

Foo foo = new Foo();
ILogger logger = new LoggerToDb();
foo.setLoggerImpl(logger);
foo.doStuff();

请注意,在您的实现中没有什么需要改变Foo,因为Foo它不依赖于Logger,而是ILogger- 使用这种方法,我们可以为抽象提供一个新的具体化,并将其交换为Foo甚至无需接触Foo!漂亮整洁的国际海事组织。

请注意,在上面的示例中,我正在构建对象并提供实现,这也可以使用诸如 Java 的 Spring 之类的 IOC 框架来完成。

于 2015-04-21T17:05:02.987 回答
3

虚线表示源代码依赖项。带空三角形的实线表示一种特殊的依赖类型:类继承或接口实现。

在此图中,Policy Service 和 Mechanism Service 是抽象,Policy Service 是更高级别的抽象。机制和效用是细节。

Policy 对 Mechanism 和 Utility 有明显的运行时依赖(从 Policy 开始的控制流会到达这些细节),而在较低级别的 Mechanism 对 Utility 有类似的运行时依赖。但是,在源码层面,Policy 只依赖于 Policy Service,而 Mechanism 也依赖于 Policy Service。这就是依赖倒置。

于 2015-04-21T17:30:49.240 回答