什么是Ruby中的java接口等价物?

cra*_*crv 94 ruby interface

我们可以像在java中那样公开Ruby中的接口,并强制执行Ruby模块或类来实现接口定义的方法.

一种方法是使用继承和method_missing来实现相同的目标,但还有其他更合适的方法吗?

Jör*_*tag 83

Ruby拥有接口,就像任何其他语言一样.

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

区分这两者非常重要.重要的是界面,而不是interface.该interface告诉你几乎没有什么用处.没有什么比Java中的标记接口更好地证明了这一点,它们是完全没有成员的接口:只需看看java.io.Serializablejava.lang.Cloneable; 这两个interface有什么恶意非常不同的事情,但他们有完全相同的签名.

所以,如果两个interfaces表示不同的含义,具有相同的签名,有什么确切的是interface,即使你保证?

另一个好例子:

package java.util;

interface List<E> implements Collection<E>, Iterable<E> {
    void add(int index, E element)
        throws UnsupportedOperationException, ClassCastException,
            NullPointerException, IllegalArgumentException,
            IndexOutOfBoundsException;
}
Run Code Online (Sandbox Code Playgroud)

是什么接口java.util.List<E>.add

  • 集合的长度不会减少
  • 之前收集的所有物品仍然存在
  • element是在集合中

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

这是一个非常有效的实现interface:

class MyCollection<E> implements java.util.List<E> {
    void add(int index, E element)
        throws UnsupportedOperationException, ClassCastException,
            NullPointerException, IllegalArgumentException,
            IndexOutOfBoundsException {
        remove(element);
    }
}
Run Code Online (Sandbox Code Playgroud)

另一个例子:java.util.Set<E>它实际上在哪里说它是,你知道,它是一套?无处!或者更确切地说,在文档中.用英语讲.

interfacesJava和.NET的几乎所有情况下,所有相关信息实际上都在文档中,而不是在类型中.那么,如果类型不告诉你任何有趣的东西,为什么要保留它们呢?为什么不坚持文档?而这正是Ruby所做的.

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

简而言之:Ruby没有Java的等价物interface.但是,它确实具有Java 接口的等价物,它与Java:文档中的完全相同.

此外,就像在Java中一样,Acceptance Tests也可用于指定接口.

特别是,在Ruby中,对象的接口由它可以什么决定,而不是class它是什么,或module它混入什么.任何具有<<方法的对象都可以被附加到.这在单元测试中非常有用,在单元测试中,你可以简单地传入一个Array或一个String而不是一个更复杂的Logger,即使Array并且除了它们都有一个被调用的方法这一事实之外Logger不要共享.interface<<

另一个例子是StringIO,它实现了相同的接口作为IO和因此的大部分接口File,但是没有除了共享任何共同的祖先Object.

  • 虽然读得好,但我找不到有帮助的答案.它读起来就像为什么"接口"无用,缺少其使用点.可以更容易地说ruby是动态类型的,并且考虑到了不同的焦点,并使像IOC这样的概念变得不必要/不需要.如果您习惯于按合同设计,那将是一个艰难的转变.Rails可以从中受益,核心团队在最新版本中可以看到这一点. (259认同)
  • 我把它读作"有些接口毫无意义,因此接口很糟糕.为什么要使用接口?".它没有回答这个问题,而且坦率地说,听起来像是一个不了解接口是什么以及它们的好处的人. (14认同)
  • 跟进问题:在Ruby中记录*界面的最佳方法是什么?Java关键字`interface`可能不会提供所有相关信息,但它确实提供了放置文档的明显位置.我在Ruby中编写了一个实现(足够)IO的类,但是我通过反复试验来完成它并且对这个过程不太满意.我还编写了我自己的接口的多个实现,但是记录了需要哪些方法以及它们应该做什么,以便我的团队的其他成员可以创建实现证明是一个挑战. (12认同)
  • 通过引用在名为"add"的函数中执行删除的方法,关于List接口无效性的论证是reductio ad absurdum参数的典型示例.特别是在任何语言(包括ruby)中都可以编写一种方法,该方法可以做出与预期不同的方法.这不是针对"接口"的有效参数,它只是错误的代码. (12认同)
  • `interface`*construct*确实只需要在静态类型的单继承语言中将不同的类型视为相同(例如将`LinkedHashSet`和`ArrayList`视为`Collection`),它几乎没有什么用*接口*如此答案所示.Ruby不是静态类型的,因此不需要*construct*. (9认同)
  • 我不在乎我上面27个人的事情,先生,你已经钉了它. (2认同)
  • @Oddman:我不确定你认为我说接口坏了.接口非常重要.这就是为什么Java的`interface`关键字在捕获界面的想法方面非常糟糕,这真是太遗憾了. (2认同)
  • 我认为许多开发人员常常忽略的是,代码与其他开发人员的交流与与计算机的交流一样多。对我来说,interface 关键字是向其他开发人员发出的一个注释,表明您正在尝试遵守所定义的接口构造。是的,您可以做一些愚蠢的事情,例如删除列表成员而不是添加它,但这有点没有抓住要点,不是吗?我发现接口构造根本没有用,我发现知道对于给定模式可以使用哪些类非常有用。生活很少有保证,包括软件实现。 (2认同)
  • 最好说“Ruby 根本没有接口,接受它吧!” (2认同)

Jar*_*eck 47

试试rspec的"共享示例":

https://www.relishapp.com/rspec/rspec-core/v/3-5/docs/example-groups/shared-examples

你为你的界面写了一个规范,然后在每个实现者的规范中加上一行,例如.

it_behaves_like "my interface"
Run Code Online (Sandbox Code Playgroud)

完整的例子:

RSpec.shared_examples "a collection" do
  describe "#size" do
    it "returns number of elements" do
      collection = described_class.new([7, 2, 4])
      expect(collection.size).to eq(3)
    end
  end
end

RSpec.describe Array do
  it_behaves_like "a collection"
end

RSpec.describe Set do
  it_behaves_like "a collection"
end
Run Code Online (Sandbox Code Playgroud)

  • 我相信这应该是公认的答案.这是大多数弱类型语言可以提供类似Java接口的方式.接受的解释了为什么Ruby没有接口,而不是如何模仿它们. (10认同)
  • 我同意,作为 Java 开发人员转向 Ruby,这个答案对我的帮助比上面接受的答案要大得多。 (2认同)

car*_*yam 38

我们可以像在java中那样公开Ruby中的接口,并强制执行 Ruby模块或类来实现接口定义的方法.

Ruby没有这个功能.原则上,它不需要它们,因为Ruby使用所谓的duck typing.

您可以采取的方法很少.

编写引发异常的实现; 如果子类尝试使用未实现的方法,它将失败

class CollectionInterface
  def add(something)
    raise 'not implemented'
  end
end
Run Code Online (Sandbox Code Playgroud)

除此之外,你应该编写执行合同的测试代码(这里的其他帖子错误地称为接口)

如果你发现自己一直在编写像上面这样的void方法,那么写一个捕获它的辅助模块

module Interface
  def method(name)
    define_method(name) { |*args|
      raise "interface method #{name} not implemented"
    }
  end
end

class Collection
  extend Interface
  method :add
  method :remove
end
Run Code Online (Sandbox Code Playgroud)

现在,将上面的内容与Ruby模块结合起来,你就可以得到你想要的东西......

module Interface
  def method(name)
    define_method(name) { |*args|
      raise "interface method #{name} not implemented"
    }
  end
end

module Collection
  extend Interface
  method :add
  method :remove
end

col = Collection.new # <-- fails, as it should
Run Code Online (Sandbox Code Playgroud)

然后你就可以做到

class MyCollection
  include Collection

  def add(thing)
    puts "Adding #{thing}"
  end
end

c1 = MyCollection.new
c1.add(1)     # <-- output 'Adding 1'
c1.remove(1)  # <-- fails with not implemented
Run Code Online (Sandbox Code Playgroud)

让我再次强调:这是一个基本的,因为Ruby中的所有内容都在运行时发生; 没有编译时间检查.如果您将此与测试相结合,那么您应该能够发现错误.更进一步,如果你进一步采取上述步骤,你可能能够编写一个接口,在第一次创建该类的对象时执行对类的检查; 让你的测试像调用一样简单MyCollection.new...是的,超过顶部:)


fot*_*nus 9

正如大家都说的那样,红宝石没有接口系统.但是通过内省,你可以很容易地自己实现它.这是一个简单的示例,可以通过多种方式进行改进,以帮助您入门:

class Object
  def interface(method_hash)
    obj = new
    method_hash.each do |k,v|
      if !obj.respond_to?(k) || !((instance_method(k).arity+1)*-1)
        raise NotImplementedError, "#{obj.class} must implement the method #{k} receiving #{v} parameters"
      end
    end
  end
end

class Person
  def work(one,two,three)
    one + two + three
  end

  def sleep
  end

  interface({:work => 3, :sleep => 0})
end
Run Code Online (Sandbox Code Playgroud)

删除在Person上声明的方法之一或更改它的参数数量将引发一个NotImplementedError.


fif*_*uri 5

用Java的方式没有接口之类的东西。但是,您还可以在红宝石中享受其他乐趣。

如果要实现某种类型和接口(以便可以检查对象是否具有所需的某些方法/消息),则可以查看rubycontracts。它定义了类似于PyProtocols的机制。关于ruby中类型检查的博客在这里

上面提到的方法并不是活着的项目,尽管起初目标似乎不错,但似乎大多数红宝石开发人员都可以活着而无需严格的类型检查。但是ruby的灵活性使得可以执行类型检查。

如果您想通过某些行为来扩展对象或类(在ruby中是相同的东西)或某种程度上具有ruby的多重继承方式,请使用includeor extend机制。随着include您可以包括从另一个类或模块的方法为对象。使用,extend您可以为类添加行为,以便其实例具有添加的方法。不过,这只是一个简短的解释。

我认为解决Java接口需求的最佳方法是了解ruby对象模型(例如,参见Dave Thomas的讲座)。可能您会忘记Java接口。或者您在日程安排中有一个特殊的应用程序。


Ale*_*ohl 5

正如许多答案所表明的那样,Ruby中没有办法通过从类(包括模块或类似的东西)继承来强制类实现特定的方法。原因可能是TDD在Ruby社区中盛行,这是定义接口的另一种方式-测试不仅指定方法的签名,而且指定行为。因此,如果要实现另一个类,该类实现一些已经定义的接口,则必须确保所有测试都通过。

通常,使用模拟和存根来独立定义测试。但是,还有像Bogus这样的工具,可以用来定义合同测试。这样的测试不仅定义了“主要”类的行为,而且还检查了协作类中是否存在存根方法。

如果您真的关心Ruby中的接口,我建议您使用实现合同测试的测试框架。