23

我目前正在尝试学习 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();
   }
}
4

6 回答 6

25

ruby 中没有接口,因为 ruby​​ 是一种动态类型语言。接口基本上用于使不同的类可以互换,而不会破坏类型安全。您的代码可以与每个控制台一起使用,只要它的行为类似于在 C# 中表示实现 IConsole 的控制台。“duck typing”是一个关键字,您可以使用它来赶上处理此类问题的动态语言方式。

此外,您可以并且应该编写单元测试来验证代码的行为。每个对象都有一个respond_to?可以在断言中使用的方法。

于 2010-08-17T18:10:28.037 回答
14

Ruby与任何其他语言一样具有接口。

请注意,您必须注意不要将接口的概念混为一谈,它是一个单元的职责、保证和协议的抽象规范,interface它是 Java、C# 和 VB.NET 编程中的关键字的概念语言。在 Ruby 中,我们一直使用前者,但后者根本不存在。

区分两者非常重要。重要的是接口,而不是interface. interface告诉你几乎没有什么有用的。没有什么比 Java 中的标记接口更能说明这一点了,它们是完全没有成员的接口:看看java.io.Serializableand java.lang.Cloneable; 这两个interfaces 意味着非常不同的东西,但它们具有完全相同的签名。

那么,如果两个interface表示不同事物的 s 具有相同的签名,那么究竟是什么来interface保证你呢?

另一个很好的例子:

interface ICollection<T>: IEnumerable<T>, IEnumerable
{
    void Add(T item);
}

什么是接口System.Collections.Generic.ICollection<T>.Add

  • 集合的长度不会减少
  • 之前收藏中的所有物品都还在那里
  • item是在集合中

其中哪些实际上出现在interface?没有任何!没有任何内容interface表明该Add方法甚至必须添加,它也可以从集合中删除一个元素。

这是一个完全有效的实现interface

class MyCollection<T>: ICollection<T>
{
    void Add(T item)
    {
        Remove(item);
    }
}

另一个例子:java.util.Set<E>它实际上在哪里说它是一个集合?无处!或者更准确地说,在文档中。用英语。

在几乎所有的情况下interfaces,无论是来自 Java 还是 .NET,所有相关信息实际上都在文档中,而不是在类型中。所以,如果这些类型无论如何都不能告诉你任何有趣的事情,为什么还要保留它们呢?为什么不只关注文档?而这正是 Ruby 所做的。

请注意,还有其他语言实际上可以以有意义的方式描述接口。但是,这些语言通常不调用描述接口interface”的构造,而是调用它type。例如,在依赖类型的编程语言中,您可以表达以下属性:sort函数返回与原始集合长度相同的集合,原始集合中的每个元素也在排序集合中,并且之前没有更大的元素出现一个较小的元素。

所以,简而言之:Ruby 没有与 Java 等价的东西interface。但是,它确实具有与 Java Interface等效的功能,并且与 Java: 文档中的完全相同。

此外,就像在 Java 中一样,验收测试也可用于指定Interface

特别是,在 Ruby 中,对象的接口取决于它可以做什么,而不是它class是什么,或者module它混合了什么。任何具有<<方法的对象都可以附加到。这在单元测试中非常有用,您可以简单地传入 an或Arraya而不是String更复杂的.LoggerArrayLoggerinterface<<

另一个例子是StringIO,它实现了与 的接口相同的接口IO,因此实现了大部分接口File但除此之外不共享任何共同的祖先Object

于 2010-08-17T22:23:41.550 回答
5

接口通常被引入静态类型的 OO 语言,以弥补多重继承的不足。换句话说,它们更像是一种必要的邪恶,而不是本身有用的东西。

另一方面,红宝石:

  1. 是带有“鸭子类型”的动态类型语言,所以如果你想foo在两个对象上调用方法,它们既不需要继承相同的祖先类,也不需要实现相同的接口。
  2. 通过 mixins 的概念支持多重继承,这里也不需要接口。
于 2010-08-17T19:41:54.103 回答
4

Ruby 并没有它们。接口和合约通常更多地存在于静态世界中,而不是动态世界中。

如果你真的需要的话,有一个叫做Handshake的 gem可以实现非正式的合约。

于 2010-08-17T18:11:51.583 回答
0

Ruby 使用模块的概念作为接口的替代(有点)。Ruby 中的设计模式有很多很好的例子来说明这两个概念之间的差异以及为什么 ruby​​ 选择更灵活的接口替代方案。

http://www.amazon.com/Design-Patterns-Ruby-Russ-Olsen/dp/0321490452

于 2010-08-17T18:34:02.610 回答
0

Jorg 有一个好处,ruby 有接口,只是没有关键字。在阅读一些回复时,我认为这在动态语言中是负面的。您必须创建单元测试而不是让编译器捕获未实现的方法,而不是通过语言强制执行接口。这也使得理解方法更难推理,因为当你试图调用一个对象时,你必须找出它是什么。

举个例子:

def my_func(options)
  ...
end

如果您查看该函数,您不知道选项是什么以及它应该调用什么方法或属性,而无需寻找单元测试,调用它的其他地方,甚至查看方法。更糟糕的是,该方法甚至可能不使用这些选项,而是将其传递给其他方法。为什么要在编译器应该捕获到单元测试时编写单元测试。问题是您必须以不同的方式编写代码才能在动态语言中表达这种缺点。

不过,这有一个好处,那就是动态编程语言可以快速编写一段代码。我不必编写任何接口声明,以后我可以新的方法和参数而无需去接口公开它。权衡是维护速度。

于 2016-05-30T03:49:30.177 回答