我们可以像在java中那样公开Ruby中的接口,并强制执行Ruby模块或类来实现接口定义的方法.
一种方法是使用继承和method_missing来实现相同的目标,但还有其他更合适的方法吗?
Jör*_*tag 83
Ruby拥有接口,就像任何其他语言一样.
请注意,您必须小心不要混淆Interface的概念,这是一个单元的职责,保证和协议的抽象规范,其概念interface是Java,C#和VB.NET编程中的关键字.语言.在Ruby中,我们一直使用前者,但后者根本不存在.
区分这两者非常重要.重要的是界面,而不是interface.该interface告诉你几乎没有什么用处.没有什么比Java中的标记接口更好地证明了这一点,它们是完全没有成员的接口:只需看看java.io.Serializable和java.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.
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)
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...是的,超过顶部:)
正如大家都说的那样,红宝石没有接口系统.但是通过内省,你可以很容易地自己实现它.这是一个简单的示例,可以通过多种方式进行改进,以帮助您入门:
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.
用Java的方式没有接口之类的东西。但是,您还可以在红宝石中享受其他乐趣。
如果要实现某种类型和接口(以便可以检查对象是否具有所需的某些方法/消息),则可以查看rubycontracts。它定义了类似于PyProtocols的机制。关于ruby中类型检查的博客在这里。
上面提到的方法并不是活着的项目,尽管起初目标似乎不错,但似乎大多数红宝石开发人员都可以活着而无需严格的类型检查。但是ruby的灵活性使得可以执行类型检查。
如果您想通过某些行为来扩展对象或类(在ruby中是相同的东西)或某种程度上具有ruby的多重继承方式,请使用includeor extend机制。随着include您可以包括从另一个类或模块的方法为对象。使用,extend您可以为类添加行为,以便其实例具有添加的方法。不过,这只是一个简短的解释。
我认为解决Java接口需求的最佳方法是了解ruby对象模型(例如,参见Dave Thomas的讲座)。可能您会忘记Java接口。或者您在日程安排中有一个特殊的应用程序。