动态语言中的接口有什么意义吗?

时间:2008-09-18 10:59:41

标签: php interface dynamic-languages duck-typing

在像Java这样的静态语言中,你需要接口,因为 否则类型系统就不会让你做某些事情。 但是在PHP和Python这样的动态语言中你只需要 duck-typing 的优势。

PHP支持接口。 Ruby和Python没有它们。 所以如果没有他们,你可以很幸福地生活。

我一直在用PHP工作,而且从来没有真正做过 利用定义接口的能力。当我需要一个 然后,实现某些公共接口的类集 我只是在文档中描述它。

那么,您怎么看?不使用你不是更好吗? 动态语言中的接口是什么?

17 个答案:

答案 0 :(得分:17)

我认为它更像是一种便利。如果你有一个带有“类文件”对象并且只调用read()方法的函数,那么强迫用户实现某种File接口是不方便的 - 甚至是限制。检查对象是否具有读取方法同样容易。

但是如果你的函数需要大量的方法,那么检查对象是否支持接口更容易,然后检查每个方法的支持。

答案 1 :(得分:10)

是的,有一点

如果你没有明确地使用接口,你的代码仍然使用该对象,就好像它实现了某些方法一样,只是不清楚未说出的接口是什么。

如果你定义一个接受一个接口的函数(在PHP中说),那么它早先就会失败,问题将出在调用者身上而不是使用该方法完成工作。一般来说,早先失败是一个很好的经验法则。

答案 2 :(得分:9)

接口实际上为具有它们的静态语言添加了一定程度的动态类似灵活性,如Java。它们提供了一种查询对象的方法,该对象在运行时实现了

这个概念很好地融入了动态语言。当然,根据你对“动态”这个词的定义,它甚至包括Objective-C,它在Cocoa中广泛使用了Protocols。

在Ruby中,您可以询问对象是否响应给定的方法名称。但这是一个非常微弱的保证,它会做你想要的,特别是考虑到一遍又一遍地使用了很少的单词,没有考虑完整的方法签名等。

在Ruby中我可能会问

object.respond_to? :sync

所以,是的,它有一个名为“sync”的方法,无论那意味着什么。

在Objective-C中我可能会问类似的东西,即“看起来/走路/嘎嘎像是同步的东西吗?”:

[myObject respondsToSelector:@selector(sync)]

更好的是,以一些冗长的代价,我可以提出一些更具体的内容,即“看起来/走路/嘎嘎就像是与MobileMe同步的东西吗?”:

[myObject respondsToSelector:@selector(sync:withMobileMeAccount:)]

这是鸭子打字到物种水平。

但要真正询问一个对象是否有希望实现与MobileMe的同步......

[receiver conformsToProtocol:@protocol(MobileMeSynchronization)]

当然,您可以通过检查是否存在一系列选择器来实现协议,这些选择器您认为是协议/ duck的定义,以及它们是否足够具体。在这一点上,协议只是一大堆丑陋的responds_to的缩写?查询,以及编译器/ IDE使用的一些非常有用的语法糖。

接口/协议是对象元数据的另一个维度,可用于在处理这些对象时实现动态行为。在Java中,编译器恰好要求正常的方法调用。但即使是像Ruby,Python,Perl等动态语言也实现了一种类型的概念,它不仅仅是“对象响应的方法”。因此class关键字。 Javascript是唯一没有这个概念的常用语言。如果你有课程,那么接口也是有意义的。

对于更复杂的库或类层次结构而言,它比大多数应用程序代码更有用,但我认为这个概念在任何语言中都很有用。

另外,有人提到了mixins。 Ruby mixins是一种共享代码的方式 - 例如,它们与类的实现有关。接口/协议是关于类或对象的接口。它们实际上可以相互补充。您可能有一个指定行为的接口,以及一个或多个mixins,它们可以帮助对象实现该行为。

当然,我想不出任何真正兼具一流语言功能的语言。在mixin中,包括mixin通常意味着它实现的接口。

答案 3 :(得分:4)

如果您没有高安全性限制(因此没有人会以您不想要的方式访问您的数据)并且您拥有良好的文档或训练有素的编码器(因此他们不需要解释器/编译器来告诉您他们该怎么做),然后不,这没用。

对于大多数中等规模的项目,只需要鸭子打字。

答案 4 :(得分:3)

我认为接口的使用更多地取决于有多少人将使用您的库。如果它只是你或一个小团队,那么文档和惯例将会很好,并且要求接口将是一个障碍。如果它是一个公共库,那么接口更有用,因为它们限制人们提供正确的方法而不仅仅是提示。因此,接口绝对是编写公共库的一个有价值的特性,我认为缺乏(或至少不再强调)是动态语言更多地用于应用程序而强类型语言用于大型库的众多原因之一。 / p>

答案 5 :(得分:3)

我的印象是Python doesn't have interfaces。据我所知,在Python中你无法强制实现一个在编译时实现的方法,因为它是一种动态语言。

Python有接口库,但我没有使用它们。

Python也有Mixins,所以你可以通过为每个方法实现定义一个具有pass的Mixin来创建一个Interface类,但这并不能给你带来太多价值。

答案 6 :(得分:2)

Rene,请求read my answer在StackOverflow上提出“动态语言中构建大型系统的最佳实践”问题。我讨论了放弃动态语言自由的一些好处,以节省开发工作并简化新程序员引入项目。如果使用得当,接口极大地有助于编写可靠的软件。

答案 7 :(得分:2)

在像PHP这样的语言中,一个不存在的方法调用会导致致命的错误并使整个应用程序失效,那么是的接口是有意义的。

在Python这样的语言中,你可以捕获并处理无效的方法调用,但事实并非如此。

答案 8 :(得分:2)

Python 3000将有Abstract Base Classes。非常值得一读。

答案 9 :(得分:1)

Java“接口”的一个用途是允许Java中的强类型混合。您可以混合使用适当的超类,以及为支持接口而实现的任何其他方法。

Python具有多重继承,因此它不需要接口设计来允许来自多个超类的方法。

然而,我喜欢强类型的一些好处 - 主要是,我是早期错误检测的粉丝。我尝试使用“类似接口”的抽象超类定义。

class InterfaceLikeThing( object ):
    def __init__( self, arg ):
        self.attr= None
        self.otherAttr= arg
    def aMethod( self ):
        raise NotImplementedError
    def anotherMethod( self ):
        return NotImplemented

这在某种程度上规范了界面。它没有为符合期望的子类提供绝对证据。但是,如果子类未能实现所需的方法,则单元测试将失败,并显示NotImplemented返回值或NotImplementedError异常。

答案 10 :(得分:0)

嗯,当您调用初始方法中使用的一个或两个方法时,检查给定对象是否支持整个接口肯定会更容易,而不仅仅是不会崩溃将对象添加到内部列表。

鸭子打字具有界面的一些好处,即易于使用,但检测机制仍然缺失。

答案 11 :(得分:0)

这就像说你不需要动态类型语言中的显式类型。为什么不把所有东西都变成“var”并在其他地方记录它们的类型?

这是程序员对程序员施加的限制。这让你更难以在脚下射击自己;为您提供更少的错误空间。

答案 12 :(得分:0)

嗯,首先,Ruby没有接口是正确的,但是他们有mixin,它在某种程度上取得了其他语言中接口和抽象类的最佳效果。

接口的主要目标是确保您的对象能够实现接口本身中存在的所有方法。

当然,接口永远不是强制性的,即使在Java中,你可以想象只使用类和使用反射调用方法,当你不知道你正在操作的那种对象时,它是容易出错的,应该在许多方面都气馁。

答案 13 :(得分:0)

作为PHP程序员,我看到它的方式,接口基本上用作合同。它可以让你说使用这个接口的所有东西都必须实现一组给定的函数。

我不知道这是否有用,但我发现在尝试理解接口的全部内容时,它有点绊脚石。

答案 14 :(得分:0)

如果您觉得必须这样做,您可以实现一种带有将对象的方法/属性与给定签名进行比较的函数的接口。这是一个非常基本的例子:

file_interface = ('read', 'readline', 'seek')

class InterfaceException(Exception): pass

def implements_interface(obj, interface):
    d = dir(obj)
    for item in interface:
        if item not in d: raise InterfaceException("%s not implemented." % item)
    return True

>>> import StringIO
>>> s = StringIO.StringIO()
>>> implements_interface(s, file_interface)
True
>>> 
>>> fp = open('/tmp/123456.temp', 'a')    
>>> implements_interface(fp, file_interface)
True
>>> fp.close()
>>> 
>>> d = {}
>>> implements_interface(d, file_interface)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 4, in implements_interface
__main__.InterfaceException: read not implemented.

当然,这并不能保证很多。

答案 15 :(得分:0)

除了其他答案之外,我只想指出Javascript有一个instanceof关键字,如果给定实例在给定对象的原型链中的任何位置,它将返回true。

这意味着如果您在原型链中使用“接口对象”作为“实现对象”(两者都只是JS的普通对象),那么您可以使用instanceof来确定它是否“实现”它。这对执行方面没有帮助,但它确实有助于多态方面 - 这是接口的一种常见用途。

MDN instanceof Reference

答案 16 :(得分:-2)

停止尝试用动态语言编写Java。