26

在像 Java 这样的静态语言中,您需要接口,因为否则类型系统不会让您做某些事情。但在 PHP 和 Python 等动态语言中,您只需利用鸭子类型

PHP 支持接口。Ruby 和 Python 没有它们。因此,没有它们,您显然可以幸福地生活。

我大部分时间都在使用 PHP 进行工作,但从未真正使用过定义接口的能力。当我需要一组类来实现某个通用接口时,我只是在文档中描述它。

所以你怎么看?完全不使用动态语言的接口不是更好吗?

4

17 回答 17

18

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

但是,如果您的函数需要大量方法,则检查对象是否支持接口然后检查每个单独方法的支持会更容易。

于 2008-09-18T11:16:30.410 回答
10

是的,有一点

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

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

于 2008-09-18T11:10:52.530 回答
9

接口实际上为拥有它们的静态语言(如 Java)增加了某种程度的动态类语言灵活性。它们提供了一种查询对象在运行时实现了哪些合约的方法。

这个概念很好地移植到动态语言中。当然,这取决于您对“动态”一词的定义,甚至包括 Objective-C,它在 Cocoa 中非常广泛地使用了协议。

在 Ruby 中,您可以询问对象是否响应给定的方法名称。但这是一个非常薄弱的​​保证,它会做你想做的事,特别是考虑到反复使用的单词很少,不考虑完整的方法签名等等。

在 Ruby 中,我可能会问

object.respond_to? :sync

所以,是的,它有一个名为“sync”的方法,不管这意味着什么。

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

[myObject respondsToSelector:@selector(sync)]

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

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

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

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

[receiver conformsToProtocol:@protocol(MobileMeSynchronization)]

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

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

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

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

当然,我想不出有哪一种语言真正兼具两种不同的一流语言特征。在使用 mixin 的那些中,包括 mixin 通常意味着它实现的接口。

于 2008-09-18T13:15:11.870 回答
4

如果您没有高安全性约束(因此没有人会以您不希望的方式访问您的数据)并且您拥有良好的文档或训练有素的编码人员(因此他们不需要解释器/编译器来告诉他们要做什么做),那么不,它没用。

对于大多数中型项目,鸭式打字就是您所需要的。

于 2008-09-18T11:46:57.043 回答
3

我的印象是Python 没有接口。据我所知,在 Python 中,您不能强制执行在编译时实现的方法,因为它是一种动态语言。

有 Python 的接口库,但我一个都没用过。

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

于 2008-09-18T11:14:05.267 回答
3

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

于 2008-09-18T11:39:10.120 回答
2

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

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

于 2008-09-18T15:09:14.577 回答
2

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

于 2008-09-18T16:11:03.727 回答
2

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

于 2008-09-18T11:27:24.830 回答
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异常。

于 2008-09-18T12:41:54.790 回答
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.

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

于 2008-09-19T12:30:25.220 回答
0

好吧,首先,Ruby 没有接口是正确的,但是它们有 mixin,它以某种方式从其他语言中汲取了接口和抽象类的精华。

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

当然,接口从来都不是强制性的,即使在 Java 中,您也可以想象只使用类并在您不知道要操作哪种对象时使用反射来调用方法,但它很容易出错,应该在很多种方法。

于 2008-09-18T11:04:13.247 回答
0

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

鸭打字有一些接口的好处,就是到处都好用,但是检测机制还是缺失的。

于 2008-09-18T11:15:53.763 回答
0

这就像说您不需要动态类型语言中的显式类型。为什么不将所有内容都设为“var”并在其他地方记录它们的类型?

这是程序员强加给程序员的限制。这让你更难射中自己的脚;给你更少的错误空间。

于 2008-09-18T11:37:14.710 回答
0

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

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

于 2008-09-18T13:36:52.910 回答
0

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

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

MDN 实例参考

于 2012-12-06T00:49:32.263 回答
-2

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

于 2009-03-27T08:34:10.030 回答