这可能会澄清一些事情:
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 框架来完成。