【问题标题】:In Ruby, what is the equivalent to an interface in C#?在 Ruby 中,什么相当于 C# 中的接口?
【发布时间】:2011-03-31 03:36:05
【问题描述】:

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

【问题讨论】:

  • 在我看来,没有一种语言的接口是一个可怕的设计决定。
  • 在 Ruby(以及所有语言)中有个接口——它们只是不是一种语言特性,因此编译器不会强制执行。同样,我可以说关于 C/C++/Java/C#/... 的“语言中没有整数对我来说似乎是一个可怕的设计决定”——如果重要的话,你会得到构建它们的工具你。但大多数时候,我们只是在一个非常小的整数子集上使用模运算,而我们的软件通常可以正常工作。没有人死。
  • 很确定您在插入墨盒之前忘记吹气......

标签: c# ruby interface contract equivalent


【解决方案1】:

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

举个例子:

def my_func(options)
  ...
end

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

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

【讨论】:

  • 请检查此URL 它将有助于提高您的质量
【解决方案2】:

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

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

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

那么,如果两个interfaces 意味着不同的东西,具有相同的签名,那么interface 到底是什么

另一个很好的例子:

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

System.Collections.Generic.ICollection&lt;T&gt;.Add接口是什么?

  • 集合的长度不会减少
  • 之前集合中的所有项目仍然存在
  • item 在集合中

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

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

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

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

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

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

因此,简而言之:Ruby 没有与 Java interface 等效的对象。但它确实有一个等价于Java接口,并且它与Java中的完全相同:文档。

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

特别是,在 Ruby 中,对象的 接口 取决于它可以做什么,而不是 class 是什么,或者 module 它是什么混入。任何具有&lt;&lt; 方法的对象都可以附加到。这在单元测试中非常有用,您可以简单地传入ArrayString 而不是更复杂的Logger,即使ArrayLogger 不共享显式interface因为他们都有一个名为&lt;&lt;的方法。

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

【讨论】:

  • 是的,文档可以描述一个类应该做什么,但它不能保证任何事情。通过接口实现,您可以确定至少存在 Add(item) 方法。它是否执行您期望它执行的功能是无关紧要的。你知道,如果你调用 Add(item),它就会在那里并且会做一些事情。以上听起来像是一个松散的宗教论点。
  • @Ian 如果您希望通过接口满足的要求是确保对象具有 Add 方法,respond_to?将是实现这一目标的 Ruby 方式。
  • 我同意 Ian 的观点,接口(如 Java 等语言所包含的)的重点是为类提供方便的契约。当一个类说它实现了一个接口时,我肯定会有一个名为“Add”的方法,我很可能会用它来添加东西。是的,文档也会告诉我,但这不是重点。我可以在没有任何行空间的情况下修改代码并说“阅读文档”以了解此类的作用。话虽如此,Tom 对使用 respond_to 的评论?将是要走的路。但是不方便。
  • 为什么使用respond_to不方便?您可以轻松编写单元测试,只测试某个类的接口。
  • @IanSuttle 实际上,它比这更好,因为“已实现”方法的存在表明有义务正确实现它:也就是说,如果它没有做预期的事情,那么该类型可以客观上被描述为“错误”。这正是我对Structural Typing 持低评价的原因,因为意图变得隐含,而不是明确。我认为这个答案完全忽略了类型化接口的好处。
【解决方案3】:

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

另一方面,Ruby:

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

【讨论】:

  • 组合优于继承,这是要走的路。
【解决方案4】:

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

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

【讨论】:

  • 诚然,能够说 [respond_to? ],其中 表示一些操作的集合。对于期望对象列表作为支持预期操作(添加、删除、查找、长度等)的输入的方法,它可以验证它的输入确实满足这些要求,而无需检查每个要求。有问题的对象不需要是任何类型的对象,只需支持“那个接口”声明的操作即可。
  • @myself - 似乎完全可以定义一个 respond_to? type 命令针对“接口”(方法列表)进行验证,该“接口”(方法列表)仅迭代“接口”中的方法,如果都满足则返回 true。
  • 动态类型与此无关。看看php,它是动态的并且有接口。为什么 ruby​​ 的人不能接受语言中缺少一些特性?缺少另一个功能:类型提示。
  • @ChocoDeveloper Wat?如果您真的需要/想要它们,您可以实现您想要的功能。 Ruby 允许您这样做,而 PHP 不允许。
【解决方案5】:

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

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

【讨论】:

  • 接口没有功能,模块有。
  • 接口不是模块。两者都是可组合的,但这就是它们的共同点。
【解决方案6】:

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

如果你真的需要,有一个名为 Handshake 的 gem 可以实现非正式合同。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2023-04-07
    • 2013-01-10
    • 1970-01-01
    • 1970-01-01
    • 2014-09-28
    • 2011-01-14
    • 2011-10-31
    • 1970-01-01
    相关资源
    最近更新 更多