我在项目中有如下代码。这里通过使用接口进行转换,我们将调用服务IAuthenticateService的方法authenticate()。现在有5个子类具有相同的方法实现和实现相同的接口IAuthenticateService。通过查看代码,我将如何知道调用了哪个类方法实现?我对界面设计有点困惑。
((IAuthenticateService) AuthServiceApp.getInstance().getContext()
.getService(ServiceEnum.CredentialService.name()))
.authenticate(inputParams);
答案 0 :(得分:3)
通过阅读此代码,您无法知道。
但是,程序将在运行时知道要调用哪个实现:AuthServiceApp.getInstance().getContext()
返回的对象将具有一个类型,该类型将具有方法getService
的单个实现,并且此实现将被称为。
作为程序员,您不需要了解更多信息。 programming by contract范例允许您不必担心将调用哪个实现。您需要知道的是,在特定环境下,您将获得一个上下文实例,您可以在其中调用getService()
,它将为您提供服务。
其余的是细节,你不必担心。
当然,在调试时,这是一个不同的故事:你想知道哪个实现被执行,因为它可能是错误的。在这种情况下,只需按照调试器查看实际执行的代码,否则,您不应该关心,这就是多态性的所有内容:获取抽象。
答案 1 :(得分:0)
嗯,你无法分辨出返回IAuthentication的实现是接口的全部要点。我们使用接口将代码的各个部分分开。这使程序更具可扩展性和通用性,从而推动代码重用。它是一个非常强大的概念,是现代软件开发的基石。客户端(使用接口方法的代码)不关心接口另一端的内容(即接口的实现者)。这允许客户端在运行时更改。你不知道的事实是它的力量。为了理解这个概念,你必须考虑编译器如何工作以及它在编译代码时的作用。
在编译语言中,编译器将源代码转换为机器指令。当它将方法转换为机器代码时,该方法在内存中接收该方法开始的地址。当另一段代码调用该方法时,内存地址写在客户端代码的代码中。该内存地址是固定的,因此该段代码不能在运行时调用任何其他方法。它总是会调用一个方法,而不是一个不同的方法。
例如说我们有类似的东西:
private int someMethod() { ... }
编译器说someMethod位于003.所以当它编译这样的代码时:
public myMethod() {
this.someMethod();
}
它说myMethod调用someMethod,并在内存中查找someMethod所在的位置。大致它会写出类似的东西:
// myMethod
call 003
现在,方法调用(也就是调用站点)只能永远调用someMethod。它永远不会调用任何其他方法,但确切的方法。
但是在OO语言中,我们可以改变在运行时调用的实际方法。这意味着编译器在编译类时无法将该内存地址写入调用者代码。相反,它必须在运行时查找该方法地址。它是如何通过在运行时传递的对象中按名称查找方法来实现的。所以编译器可能会这样做:
// myMethod
methodAddress = this.methodsAddresses['someMethod']
call methodAddress
这是查找(有时称为虚拟指针表),它允许方法根据someObject指向的内容进行更改,并且该查找允许它在运行时发生变化。
在您需要调试某些内容之前,这一切都很好。如果您尝试调试最简单的东西,如果您使用调试器,并在客户端代码中删除断点,您可以轻松地查看其他许多内容以及插入代码。您也可以打印someObject.getClass()。getName()来查找名称,但如果您正在调试,那只是开始。