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

时间:2020-03-06 14:20:47  来源:igfitidea点击:

在像Java这样的静态语言中,我们需要接口,因为
否则类型系统将不允许我们做某些事情。
但是在动态语言(如PHP和Python)中,
鸭式打字的优势。

PHP支持接口。
Ruby和Python没有它们。
因此,没有他们,我们显然可以过上幸福的生活。

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

所以你怎么看?如果不使用,我们会更好吗
界面完全不是动态语言吗?

解决方案

好吧,首先,是对的,Ruby本身并没有接口,但是它们具有混合功能,这在某种程度上兼顾了接口和其他语言的抽象类的优点。

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

当然,接口从来都不是强制性的,即使在Java中,我们也可以想象仅在不知道要处理的对象类型时,仅使用类并使用反射来调用方法,但是这种方法容易出错,因此不建议使用很多方法。

是的,有一点

如果我们未明确使用接口,则代码仍将使用该对象,就好像它实现了某些方法一样,只是不清楚什么是隐语接口。

如果我们定义一个函数来接受一个接口(用PHP表示),那么它会更早地失败,并且问题将出在调用者而不是工作方法上。通常,较早失败是遵循的良好经验法则。

我给人的印象是Python没有接口。据我所知,在Python中,我们不能完全在编译时强制实施一种方法,因为它是一种动态语言。

有适用于Python的接口库,但我从未使用过它们。

Python还具有Mixins,因此我们可以通过为每个方法实现定义一个具有pass的Mixin来创建Interface类,但这并没有真正给我们带来太多价值。

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

鸭子输入具有接口的一些优点,即在任何地方都易于使用,但是检测机制仍然缺失。

我认为它更多是为了方便。如果我们有一个采用"类似于文件"的对象的函数,并且仅在其上调用read()方法,那么即使是强制用户实施某种File接口的限制也很不方便。检查对象是否具有读取方法也很容易。

但是,如果函数期望使用大量方法,则检查对象是否支持接口然后检查每个方法的支持会更容易。

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

这就像说我们不需要动态类型语言中的显式类型。我们为什么不将所有内容都设为" var"并在其他位置记录其类型?

这是程序员对程序员的一种限制。这使我们很难用脚射击自己。给我们更少的出错空间。

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

如果我们没有很高的安全性约束(因此没有人会以我们不想使用的方式访问数据),并且我们拥有良好的文档或者训练有素的编码人员(因此他们不需要解释器/编译器来告诉他们如何处理)做),那么不,这是没有用的。

对于大多数中等规模的项目,只需要键入鸭子即可。

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"异常。

实际上,接口向具有Java的静态语言增加了某种程度的动态lang-like灵活性。它们提供了一种查询对象的方式,该对象在运行时为其实现了契约。

这个概念很好地移植到了动态语言中。当然,根据我们对"动态"一词的定义,甚至包括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)]

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

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

公认的是,对于更复杂的库或者类层次结构,它比大多数应用程序代码更有用,但我认为该概念对任何语言都有用。

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

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

作为一个PHP程序员,按照我的看法,Interface基本上用作合同。它可以让我们说使用此接口的所有内容都必须实现一组给定的功能。

我不知道这是否有用,但是在尝试了解接口的全部含义时,我发现这是一个绊脚石。

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

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

Python 3000将具有抽象基类。非常值得一读。

如果需要,可以实现一种接口,该接口具有将对象的方法/属性与给定签名进行比较的功能。这是一个非常基本的示例:

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.

当然,这并不能保证。

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