问题
I'm currently trying to learn Ruby and I'm trying to understand more about what it offers in terms of encapsulation and contracts.
In C# a contract can be defined using an interface. A class which implements the interface must fulfil the terms within the contract by providing an implementation for each method and property (and maybe other things) defined. The individual class that implements an interface can do whatever it needs within the scope of the methods defined by the contract, so long as it accepts the same types of arguments and returns the same type of result.
Is there a way to enforce this kind of thing in Ruby?
Thanks
A simple example of what I mean in 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(); } }
回答1:
There are no interfaces in ruby since ruby is a dynamically typed language. Interfaces are basically used to make different classes interchangeable without breaking type safety. Your code can work with every Console as long it behaves like a console which in C# means implements IConsole. "duck typing" is a keyword you can use to catch up with the dynamic languages way of dealing with this kind of problem.
Further you can and should write unit tests to verify the behavior of your code. Every object has a respond_to?
method you can use in your assert.
回答2:
Ruby has Interfaces just like any other language.
Note that you have to be careful not to conflate the concept of the Interface, which is an abstract specification of the responsibilities, guarantees and protocols of a unit with the concept of the interface
which is a keyword in the Java, C# and VB.NET programming languages. In Ruby, we use the former all the time, but the latter simply doesn't exist.
It is very important to distinguish the two. What's important is the Interface, not the interface
. The interface
tells you pretty much nothing useful. Nothing demonstrates this better than the marker interfaces in Java, which are interfaces that have no members at all: just take a look at java.io.Serializable and java.lang.Cloneable; those two interface
s mean very different things, yet they have the exact same signature.
So, if two interface
s that mean different things, have the same signature, what exactly is the interface
even guaranteeing you?
Another good example:
interface ICollection<T>: IEnumerable<T>, IEnumerable
{
void Add(T item);
}
What is the Interface of System.Collections.Generic.ICollection<T>.Add?
- that the length of the collection does not decrease
- that all the items that were in the collection before are still there
- that
item
is in the collection
And which of those actually shows up in the interface
? None! There is nothing in the interface
that says that the Add
method must even add at all, it might just as well remove an element from the collection.
This is a perfectly valid implementation of that interface
:
class MyCollection<T>: ICollection<T>
{
void Add(T item)
{
Remove(item);
}
}
Another example: where in java.util.Set<E> does it actually say that it is, you know, a set? Nowhere! Or more precisely, in the documentation. In English.
In pretty much all cases of interfaces
, both from Java and .NET, all the relevant information is actually in the docs, not in the types. So, if the types don't tell you anything interesting anyway, why keep them at all? Why not stick just to documentation? And that's exactly what Ruby does.
Note that there are other languages in which the Interface can actually be described in a meaningful way. However, those languages typically don't call the construct which describes the Interface "interface
", they call it type
. In a dependently-typed programming language, you can for example express the properties that a sort
function returns a collection of the same length as the original, that every element which is in the original is also in the sorted collection and that no bigger element appears before a smaller element.
So, in short: Ruby does not have an equivalent to a Java interface
. It does however have an equivalent to a Java Interface, and its exactly the same as in Java: documentation.
Also, just like in Java, Acceptance Tests can be used to specify Interfaces as well.
In particular, in Ruby, the Interface of an object is determined by what it can do, not what class
is is, or what module
it mixes in. Any object that has a <<
method can be appended to. This is very useful in unit tests, where you can simply pass in an Array
or a String
instead of a more complicated Logger
, even though Array
and Logger
do not share an explicit interface
apart from the fact that they both have a method called <<
.
Another example is StringIO, which implements the same Interface as IO
and thus a large portion of the Interface of File
, but without sharing any common ancestor besides Object
.
回答3:
Interfaces are usually introduced to static typed OO languages in order to make up for lack of multiple inheritance. In other words, they are more of a necessary evil than something useful per se.
Ruby, on the other hand:
- Is dynamically typed language with "duck typing", so if you want to call method
foo
on two objects, they don't need to neither inherit same ancestor class, nor implement the same interface. - Supports multiple inheritance through concept of mixins, again no need for interfaces here.
回答4:
Ruby doesn't really have them; interfaces and contracts generally live more in the static world, rather than the dynamic.
There is a gem called Handshake that can implement informal contracts, if you really need it.
回答5:
Ruby uses the concept of Modules as a stand-in (kinda) for interfaces. Design Patterns in Ruby has a lot of really great examples on the differences between the two concepts and why ruby chooses the more flexible alternative to interfaces.
http://www.amazon.com/Design-Patterns-Ruby-Russ-Olsen/dp/0321490452
回答6:
Jorg has a good point, ruby has interfaces, just not the keyword. In reading some of the replies, I think this is a negative in dynamic languages. Instead of enforcing an interface through the language, you must create unit tests instead of having a compiler catch methods not being implemented. It also makes understanding method harder to reason about, as you have to hunt down what an object is when you are trying to call it.
Take as an example:
def my_func(options)
...
end
If you look at the function, you have no clue what options is and what methods or properties it should call, without hunting for the unit tests, other places it is called, and even look at the method. Worse yet, the method may not even use those options but pass it to further methods. Why write unit tests when this should have been caught by a compiler. The problem is you must write code differently to express this downside in dynamic languages.
There is one upside to this though, and that is dynamic programming languages are FAST to write a piece of code. I don't have to write any interface declaration and later I can new methods and parameters without going to the interface to expose it. The trade-offs are speed for maintenance.
来源:https://stackoverflow.com/questions/3505521/in-ruby-what-is-the-equivalent-to-an-interface-in-c