我目前正在尝试学习Ruby,而我正在尝试更多地了解它在封装和合同方面提供的内容。
在C#中,可以使用接口定义合同。实现接口的类必须通过为每个定义的方法和属性(以及可能的其他内容)提供实现来满足合同中的条款。实现接口的单个类可以在合同定义的方法范围内执行任何需要,只要它接受相同类型的参数并返回相同类型的结果。
有没有办法在Ruby中强制执行此类操作?
由于
我在C#中的一个简单例子:
interface IConsole { int MaxControllers {get;} void PlayGame(IGame game); } class Xbox360 : IConsole { public int MaxControllers { get { return 4; } } public void PlayGame(IGame game) { InsertDisc(game); NavigateToMenuItem(); Click(); } } class NES : IConsole { public int MaxControllers { get { return 2; } } public void PlayGame(IGame game) { InsertCartridge(game); TurnOn(); } }
答案 0 :(得分:25)
ruby中没有接口,因为ruby是一种动态类型语言。接口基本上用于使不同的类可互换而不会破坏类型安全性。您的代码可以与每个控制台一起使用,只要它像C#中的控制台一样实现IConsole。 “duck typing”是一个关键词,你可以用它来赶上处理这类问题的动态语言方式。
此外,您可以而且应该编写单元测试来验证代码的行为。每个对象都有一个respond_to?
方法,可以在断言中使用。
答案 1 :(得分:15)
Ruby拥有接口,就像任何其他语言一样。
请注意,您必须小心不要混淆 Interface 的概念,这是一个单位的责任,保证和协议的抽象规范,具有{{1}的概念。这是Java,C#和VB.NET编程语言中的关键字。在Ruby中,我们一直使用前者,但后者根本就不存在。
区分这两者非常重要。重要的是接口,而不是interface
。 interface
告诉你几乎没什么用处。没有什么能比Java中的标记接口更好地证明这一点,它们是完全没有成员的接口:只需看看java.io.Serializable
和java.lang.Cloneable
;这两个interface
意味着非常不同的东西,但它们具有完全相同的签名。
那么,如果两个interface
表示不同的东西,具有相同的签名,那么完全是interface
甚至可以保证你吗?
另一个好例子:
interface
System.Collections.Generic.ICollection<T>.Add
的接口是什么?
interface ICollection<T>: IEnumerable<T>, IEnumerable
{
void Add(T item);
}
在集合哪些实际出现在item
?没有! interface
中没有任何内容表明interface
方法甚至必须添加,它也可能从中移除元素集合。
这是Add
的完全有效的实现:
interface
另一个例子:在java.util.Set<E>
中,它实际上是说它是集吗?无处!或者更确切地说,在文档中。用英语。
在几乎所有来自Java和.NET的class MyCollection<T>: ICollection<T>
{
void Add(T item)
{
Remove(item);
}
}
的情况下,所有相关的信息实际上都在文档中,而不是在类型中。那么,如果类型不告诉你任何有趣的东西,为什么要保留它们呢?为什么不坚持文档?而这正是Ruby所做的。
请注意,其他语言中可以实际以有意义的方式描述接口。但是,这些语言通常不会调用描述接口“interfaces
”的构造,他们称之为interface
。在依赖类型的编程语言中,您可以例如表达type
函数返回与原始集合长度相同的集合的属性,原始中的每个元素也在已排序的集合中,并且没有更大的元素出现在更小的元素之前。
因此,简而言之:Ruby没有Java sort
的等价物。它 但它具有与Java 接口等效的东西,它与Java中的完全相同:文档。
此外,就像在Java中一样, Acceptance Tests 也可用于指定 Interface 。
特别是在Ruby中,对象的接口取决于它可以做什么,而不是interface
是什么,或者是什么{{ 1}}它混入。任何具有class
方法的对象都可以附加到。这在单元测试中非常有用,您可以简单地传入module
或<<
而不是更复杂的Array
,即使String
和{{1}除了他们都有一个名为Logger
的方法之外,不要共享一个明确的Array
。
另一个示例是StringIO
,它实现与Logger
相同的接口,因此interface
的接口的很大一部分除了<<
之外,没有共享任何共同的祖先。
答案 2 :(得分:5)
接口通常被引入静态类型的OO语言,以弥补多重继承的缺失。换句话说,它们不仅仅是有用的邪恶本身。
另一方面,Ruby:
foo
,它们既不需要继承相同的祖先类,也不需要实现相同的接口。答案 3 :(得分:4)
Ruby并不真正拥有它们;接口和契约通常更多地存在于静态世界中,而不是动态的。
如果你真的需要,有一个名为Handshake的宝石可以实现非正式合同。
答案 4 :(得分:0)
Ruby使用模块的概念作为接口的替代(有点)。 Ruby中的设计模式有很多关于这两个概念之间差异的很好的例子,以及为什么ruby选择更灵活的接口替代方案。
http://www.amazon.com/Design-Patterns-Ruby-Russ-Olsen/dp/0321490452
答案 5 :(得分:0)
Jorg有一个很好的观点,ruby有接口,而不是关键字。在阅读一些回复时,我认为这在动态语言中是否定的。您必须创建单元测试,而不是使用编译器捕获方法,而不是通过语言强制执行接口。它还使理解方法更难以推理,因为当你试图调用它时你必须追捕一个对象是什么。
举个例子:
def my_func(options)
...
end
如果你看一下这个函数,你就不知道它是什么选项以及它应该调用哪些方法或属性,而不是寻找单元测试,调用其他地方,甚至看看方法。更糟糕的是,该方法甚至可能不使用这些选项,而是将其传递给其他方法。为什么在编译器捕获时应该编写单元测试。问题是你必须以不同的方式编写代码来表达动态语言的这种缺点。
虽然有一个好处,那就是动态编程语言可以快速编写一段代码。我不必编写任何接口声明,后来我可以使用新的方法和参数而无需访问接口来公开它。权衡取舍是维护的速度。