为什么Ruby人说他们不需要接口?

时间:2011-10-31 18:31:31

标签: ruby oop interface

ruby​​与其他OOP语言有什么不同(例如:PHP)会使接口变得无用吗?它有什么替代品吗?

编辑:

一些澄清:

  • 在其他语言(例如:PHP)中,您不需要“接口”(在代码级别它们不是必需的)。您可以使用它们签订合同,以改进软件的架构。因此,肯定'在红宝石中你不需要接口/用其他语言你需要接口因为XXX'是假的。

  • 不,mixins不是接口,它们是完全不同的东西(PHP 5.4实现了mixins)。你有没有使用过接口?

  • 是的,PHP是OOP。语言不断发展,欢迎来到现在。

7 个答案:

答案 0 :(得分:13)

嗯,这是一个共识,当一个对象在Ruby中传递时,它不是经过类型检查的。 Java和PHP中的接口是一种确认对象符合某个合同或“类型”的方式(因此某些内容可能是SerializableAuthorizableSequential以及您想要的任何其他内容)。

然而,在Ruby中,没有形式化的合同概念,接口将履行一些有意义的角色,因为接口一致性未在方法签名中检查。例如,请参阅Enumerable。当您将其混合到对象中时,您使用的是功能,而不是声明您的对象是Enumerable。将对象设为Enumerable的唯一好处是,定义each(&blk)后您自动获得mapselect和朋友。你可以完美地拥有一个实现Enumerable提供的所有方法的对象,但不会在模块中混合,它仍然可以工作。

例如,对于Ruby中需要IO对象的任何方法,您可以输入一些 nothing 与IO相关的内容,然后它会因错误而爆炸,或者 - 如果您实现了您的IO存根正确 - 即使您传递的对象未被声明为“IO-ish”,它也能正常工作。

背后的想法来自这样一个事实:Ruby中的对象并不是真正美化的哈希表,并且标签被打到它们上(然后它有一些额外的标签告诉解释器或编译器这个对象有接口X因此它可以用于上下文Y)但是一个响应消息的封闭实体。因此,如果一个对象响应一个特定的消息,那么它将完成合同,如果它没有响应该消息 - 那么就会出现错误。

因此缺少接口可以部分地通过模块的存在来补偿(模块可以包含您在不对呼叫者/消费者做出任何类型承诺的情况下达到的功能),部分地通过消息传递的传统而不是键入的dicts来补偿

你应该观看Jim Weirich的一些演讲,因为他广泛接触过这个主题。

答案 1 :(得分:3)

这个问题有点开放,但这是我的看法:

接口声明的目的是两件事:

  1. 向您的未来或同事宣告此课程必须具备的方法
  2. 向您的计算机声明此类必须具备的方法
  3. 如果我们首先考虑第二个目的,那么永远不会编译Ruby源代码,因此永远不会有选项来验证与接口声明的一致性,并警告开发人员不遵守。这意味着如果Ruby有一些内置的接口支持,那么在运行时之前就没有选项可以验证一致性,因为缺少实现,应用程序将会崩溃。

    回到第一个目的。代码可读性。这可能是有意义的,指定接口的正式Ruby约定可能会有所帮助。现在,您可能会使用注释或规范或 - 我可能更喜欢 - 声明模块包含来传达此信息。 E.g。

    module Shippable
    # This is an interface module. If your class includes this module, make sure it responds to the following methods
    
      # Returns an integer fixnum representing weight in grams
      def weight
        raise NotImplementedError.new
      end
    
      # Returns an instance of the Dimension class.
      def dimensions
        raise NotImplementedError.new
      end
    
      # Returns true if the entity requires special handling.
      def dangerous?
        raise NotImplementedError.new
      end
    
      # Returns true if the entity is intended for human consumption and thereby must abide by food shipping regulations.
      def edible?
        raise NotImplementedError.new
      end
    
    end
    
    class Product
      include Shippable
    end
    

    实施此接口的一种方法是创建一个规范,创建包含Shippable模块的每个类的实例,调用这四种方法并期望它们不会引发NotImplementedError

答案 2 :(得分:2)

由于ruby为duck-typed,因此不需要单独的接口,但对象只需要实现常用方法。请看下面的“经典”示例:

class Duck

  def move
    "I can waddle."
  end

end

class Bird

  def move
    "I can fly."
  end

end

animals = []
animals << Duck.new
animals << Bird.new

animals.each do |animal|
  puts animal.move
end

在此示例中,“interface”是move方法,由DuckBird类实现。

答案 3 :(得分:2)

我是'红宝石人',我想要接口或类似的东西。

不强制执行合同 - 因为强制执行任何事情都不是Ruby,并且有点破坏动态语言的意义,无论如何没有“编译”步骤来强制执行 - 但是记录客户端子类可以签订的合同选择遵守(或不符合,但如果他们选择不这样做,他们不能抱怨,如果代码不起作用)。

当我遇到这个问题时,也就是说,当我编写一个类或模块时,我希望子类提供方法,我通常会记录我希望子类提供的方法,如下所示:

module Enumerable
  def each
    raise NotImplementedError, "Subclasses must provide this method"
  end
end

这不太理想,但这是一个相当罕见的案例,对我有用。

答案 4 :(得分:0)

我相信这是因为Ruby是动态类型的,而其他语言是静态类型的。您需要在PHP中使用接口的唯一原因是在传递对象时使用类型提示。

答案 5 :(得分:-1)

Ruby是非常动态和鸭子型的。难道这不会使界面变得无用或过度杀伤吗?接口强制类在编译时使用某些方法。

同样回顾一下:

http://en.wikipedia.org/wiki/Duck_typing

答案 6 :(得分:-1)

取决于界面的含义。

如果by interface是指您继承或实现的语言中存在的具体对象,则不要使用ruby等语言中的接口。

如果你的意思是对象中的界面有一些记录良好的界面,那么当然,对象仍然有一个记录良好的界面,它们有你希望存在的属性和方法。

我同意接口是你脑海中存在的东西和文档,而不是作为对象的代码。