依赖倒置原则(适用于Java)

时间:2015-04-21 16:39:30

标签: java oop solid-principles dependency-inversion

我自学了S.O.L.I.D所涉及的原则。面向对象的编程并且无法理解字母D中的所有细节(依赖性 - 反转原理。)

我正在阅读维基百科(http://en.wikipedia.org/wiki/Dependency_inversion_principle)中的条目,并不理解图中的所有内容:

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

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

根据我目前的理解,虚线代表"工具" Java中的关键字,实线代表关键字" extends。"

这是正确的解释吗?

2 个答案:

答案 0 :(得分:3)

这可能会清除一些事情:

Explanation of the UML arrows

请注意,这些图片适用于Visual Studio,但我认为大多数信息与大多数UML文档相同。

  

虚线表示" implements"

的等价物

UML中实体的命名也让我有点......看来Policy依赖于描述与Mechanism的较低级别模块的契约的接口

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

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

最简单的例子:原始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!相当neato IMO。

请注意,在上面的示例中,我构建了对象并提供了一个实现,这也可以通过像Java的Spring这样的IOC框架来完成。

答案 1 :(得分:3)

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

在此图中,策略服务和机制服务是抽象,策略服务是更高级别的抽象。机制和效用是细节。

策略对机制和实用程序有明显的运行时依赖性(从Policy开始的控制流将达到这些细节),而在较低级别上,机制对Utility有类似的运行时依赖性。但是,在源代码级别,策略仅依赖于策略服务,而机制也依赖于策略服务。这是依赖倒置。