Ruby和鸭子打字:合同设计不可能吗?

2022-09-02 11:25:02

Java 中的方法签名:

public List<String> getFilesIn(List<File> directories)

红宝石中的类似一个

def get_files_in(directories)

在Java的情况下,类型系统为我提供了有关该方法期望和交付的信息。在Ruby的案例中,我不知道我应该传递什么,或者我期望收到什么。

在 Java 中,对象必须正式实现接口。在 Ruby 中,传入的对象必须响应此处定义的方法中调用的任何方法。

这似乎非常成问题:

  1. 即使有100%准确,最新的文档,Ruby代码也必须基本上公开其实现,从而打破封装。撇开“OO纯度”不谈,这似乎是一场维护噩梦。
  2. Ruby代码没有让我知道返回了什么;我必须进行本质上的实验,或者阅读代码,找出返回对象将响应的方法。

我不想讨论静态类型与鸭子类型,而是希望了解如何维护一个生产系统,在这个系统中,你几乎没有能力通过合同进行设计。

更新

没有人真正通过这种方法所需的文档来解决方法内部实现的暴露问题。由于没有接口,如果我不需要特定的类型,难道我不必逐项列出我可能调用的每个方法,以便调用方知道可以传入的内容吗?或者这只是一个没有真正出现的边缘案例?


答案 1

归根结底,这在Ruby中是一个不好的名字 - 让我解释一下。get_files_in

在java/C#/C++中,特别是在目标C中,函数参数是名称的一部分。在 ruby 中,它们不是。
这个花哨的术语是方法重载,它由编译器强制执行。

从这些术语中考虑它,你只是在定义一个调用的方法,你实际上并没有说它应该获取文件的内容。参数不是名称的一部分,因此您不能依赖它们来识别它。
它应该获取目录中的文件吗?驱动器?网络共享?这为它在上述所有情况下工作提供了可能性。get_files_in

如果要将其限制为目录,则要考虑此信息,则应调用方法 。或者,您可以将其作为类上的一个方法,Ruby已经为您做了get_files_in_directoryDirectory

至于返回类型,这意味着您正在返回一个文件数组。你不必担心它是一个或一个>,等等,因为每个人都只使用数组(如果他们写了一个自定义数组,他们会写它来继承内置数组)。get_filesList<File>ArrayList<File

如果你只想得到一个文件,你可以称之为等等。如果你正在做一些更复杂的事情,比如返回对象而不仅仅是字符串,那么有一个非常好的解决方案:get_fileget_first_fileFileWrapper

# returns a list of FileWrapper objects
def get_files_in_directory( dir )
end

无论如何。你不能像在java中那样在ruby中强制执行契约,但这是更广泛观点的一个子集,即你不能像在java中那样在ruby中强制执行任何东西。由于ruby更具表现力的语法,你可以更清楚地编写类似英语的代码,告诉其他人你的合同是什么(从而节省了几千个尖括号)。

我个人认为这是一个净胜。你可以利用你新发现的业余时间来编写一些规格和测试,并在一天结束时推出更好的产品。


答案 2

我认为,尽管Java方法为您提供了更多信息,但它并没有为您提供足够的信息来舒适地进行编程。
例如,字符串列表是否只是文件名或完全限定的路径?

鉴于此,你关于Ruby没有给你足够的信息的论点也适用于Java。
你仍然依赖于阅读文档,查看源代码,或者调用方法并查看其输出(当然还有不错的测试)。