Why do Ruby people say they don't need interfa

2019-04-06 11:40发布

问题:

Does ruby have something different to other OOP languages (eg: PHP) that makes interfaces useless? Does it have some kind of replacement for this?

Edit:

Some clarifications:

  • In other languages (eg: PHP), you don't "need" interfaces (they are not mandatory at code level). You use them to make a contract, to improve the architecture of the software. Therefore, the affirmation 'in ruby you don't need interfaces / in other languages you need interfaces because XXX' is false.

  • No, mixins are not interfaces, they are a complete different thing (PHP 5.4 implements mixins). Have you even used interfaces?

  • Yes, PHP is OOP. Languages evolve, welcome to the present.

回答1:

Well, it's a consensus that when an object is passed in Ruby it's not type-checked. Interfaces in Java and PHP are a way to affirm that an object complies to a certain contract or "type" (so something might be Serializable, Authorizable, Sequential and whatever else that you want).

However, in Ruby there is no formalized notion of a contract for which interfaces would fulfill some meaningful role as interface conformance is not checked in method signatures. See, for example, Enumerable. When you mix it into your object you are using its functionality as opposed to declaring that your object is Enumerable. The only benefit of having your object being Enumerable is that having defined each(&blk) you automatically get map, select and friends for free. You can perfectly have an object which implements all of the methods provided by Enumerable but does not mix in the module and it would still work.

For example, for any method in Ruby that expects an IO object you could feed in something that has nothing to do with an IO, and then it would explode with an error or - if you implemented your IO stub correctly - it will work just fine even though your passed object is not declared to be "IO-ish".

The idea behind that comes from the fact that objects in Ruby are not really glorified hash tables with a tag slapped onto them (which then have some extra tags that tell the interpreter or the compiler that this object has interface X therefore it can be used in context Y) but an enclosed entity responding to messages. So if an object responds to a specific message it fullfils the contract, and if it does not respond to that message - well then an error is raised.

So the absence of interfaces is compensated partially by the presence of Modules (which can contain functionality that you reach for without doing any type promises to the caller/consumer) and partially by the tradition of message-passing as opposed to typed dicts.

You should watch some presentations by Jim Weirich since he touches on the subject extensively.



回答2:

This question is kind of open-ended, but here is my take:

The purpose of an interface declaration is two things:

  1. Declare to your future self or colleagues what methods this class must have
  2. Declare to your computer what methods this class must have

If we take the second purpose first, Ruby source code is never compiled, so there is never an option to verify the conformance to the interface declaration and warn the developer of any failure to conform. This means that if Ruby had some built-in interface support, it wouldn't have an option to verify the conformance until runtime, where the application will crash anyway, because of the missing implementation.

So back to the first purpose. Code readability. This could make sense and a formal Ruby convention of specifying interfaces might be helpful. For now, you would probably communicate this using comments or specs or - as I would probably prefer - a declarative module inclusion. 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

A way of enforcing this interface would be by creating a spec that creates an instance of every class that includes the Shippable module, calls the four methods and expects them to not raise NotImplementedError.



回答3:

As ruby is duck-typed, no separate interface is needed, but the objects only need to implement the common methods. Look at the "classic" example below:

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

In this example, the "interface" is the move method, which is implemented by both the Duck and the Bird class.



回答4:

I'm a 'Ruby person', and I would like interfaces, or something like them.

Not to enforce a contract - because enforcing anything isn't very Ruby, and kind of defeats the point of a dynamic language, and anyway there's no "compilation" step to enforce it at - but to document contracts that client subclasses can choose to conform to (or not, although if they choose not to they can't complain if the code doesn't work).

When I'm faced with this problem, ie, when I'm writing a class or module I expect subclasses to provide methods for, I usually document the methods I expect subclasses to provide like this:

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

It's not ideal, but it's a reasonably rare case and it works for me.



回答5:

I believe it's because Ruby is dynamically typed whereas other languages are statically typed. The only reason you'd need to use an interface in PHP is when you use type hinting when passing objects around.



回答6:

Ruby is very dynamic and duck-typed. Wouldn't that make interfaces kind of useless or overkill? Interfaces force classes to have certain methods available at compile time.

Review this too:

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



回答7:

Depends what you mean by interface.

If by interface you mean a concrete object that exists in your language that you inherit from or implement then no you don't use interfaces in a language like ruby.

If you mean interface as in objects have some well documented interface then yes of course, objects still have a well documented interfaces, they have attributes and methods that you expect to be there.

I'd agree that interfaces are something that exists in your mind and the documentation and not in the code as an object.