你的困惑来自你对构图的理解。另一个拥有的对象取决于拥有对象的生命周期。这并不意味着您必须在拥有类中创建拥有的对象。
如果您在类中创建对象,则该类与创建的类紧密耦合。如果不更改创建另一个类的类,就无法交换实现。
示例:
在上图中,您有类 Client,它使用类 Server。假设这是一个组合,客户端具有服务器类型的属性。
如果您在客户端类中创建类服务器的实例,它可能如下所示:
public class Client {
private Server server;
public Client(){
this.server = new Server();
}
}
现在假设您要交换服务器的实现。您需要更改 Client 类的实现,因为交换它的唯一方法是创建另一个类的实例(可能称为 AnotherServer)。
public class Client {
private AnotherServer anotherServer;
public Client(){
this.anotherServer = new AnotherServer();
}
}
这向您表明,Client 类高度依赖于 Server 类。
要轻松更改服务器的使用实现,从而修改客户端的行为,最好将客户端组合成抽象(抽象类或接口)。这样做意味着您无法在所属类中创建所需的对象,因为您只能创建具体类。创建类意味着调用构造函数并依赖于创建的类。
实现组合(——客户端由服务器组合——)的更好方法是通过 setter 方法或构造函数注入它。像这样,您可以将实现类隐藏在接口后面。
示例:
在第二张图片中,我们保护客户端不了解服务器的具体实现。它仅取决于服务器接口。这种依赖性并不那么明显,因为客户端定义了接口。他决定服务器接口所需的功能。为了表明服务器的接口属于客户端,它被称为“ClientServer”。
要组成您的客户端,您必须在类外部为 ClientServer 接口创建具体类,并通过构造函数或 setter 方法将其注入。
...
FirstServer first = new FirstServer();
Client client = new Client(first);
client.setServer(new SecondServer());
...
这样您可以轻松地在客户端中交换使用的服务器实现,即使在运行时也是如此。
这种机制称为依赖倒置原则(DIP)。但为什么? Client 类仍然依赖于服务器接口。如果接口改变,客户端也必须改变。是的,这是正确的。但是客户端决定了他在该界面中需要哪些功能。因此,当客户说需要更改时,通常界面会更改。界面随着客户端的变化而变化。
因为具体的服务器“FirstServer”和“SecondServer”实现了接口ClientServer,所以它们也依赖于该接口。而且由于继承比组合更依赖,具体的服务器类比客户端类更依赖接口。
这就是依赖倒置的原因。具体的服务器类现在依赖于“Client-ClientServer”-conglomerate。
因此,您的问题的答案是:当您在另一个班级中创建班级时,您无法达到 DIP。但是你可以通过定义一个接口并注入继承这个接口的具体类来实现 DIP。