What is java interface equivalent in Ruby?
Asked Answered
A

10

123

Can we expose interfaces in Ruby like we do in java and enforce the Ruby modules or classes to implement the methods defined by interface.

One way is to use inheritance and method_missing to achieve the same but is there any other more appropriate approach available ?

Ambrogio answered 14/12, 2010 at 8:40 Comment(5)
Click this url lawrencesong.net/2008/01/implement-java-interface-in-rubyOpalopalesce
You should double ask Yourself why You even need this. Often enough interfaces are used just to get damn thing compiled which isn't a problem in ruby.Ulberto
This question may or may not be considered a duplicate of [ In Ruby, what is the equivalent to an interface in C#? ](StackOverflow.Com/q/3505521/#3507460).Gelderland
Why I need this ? I want to implement something you can call as "versionable" which makes the documents/files versionable but versionable using what .... For e.g. I can make it versionable using existing repository softwares like SVN or CVS. Whatever unerlying mechanism I choose it should provide some basic minimum functions. I want to use interface like thing to enforce the implementation of these bare minimum functions by any new underlying repository implementation.Ambrogio
Sandi Metz in her POODR book uses tests to document interfaces. It is really worth to read this book. As of 2015 I would say that @aleksander-pohl answer is the best.Heartwhole
T
86

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 interfaces mean very different things, yet they have the exact same signature.

So, if two interfaces that mean different things, have the same signature, what exactly is the interface even guaranteeing you?

Another good example:

package java.util;

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

What is the Interface of java.util.List<E>.add?

  • that the length of the collection does not decrease
  • that all the items that were in the collection before are still there
  • that element 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<E> implements java.util.List<E> {
    void add(int index, E element)
        throws UnsupportedOperationException, ClassCastException,
            NullPointerException, IllegalArgumentException,
            IndexOutOfBoundsException {
        remove(element);
    }
}

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 it's 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 it 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.

Thistle answered 14/12, 2010 at 13:38 Comment(23)
While a good read I don't find the answer that helpful. It reads like a dissertation on why interface is useless, missing the point of its use. It would have been easier to say that ruby is dynamically typed and that has a different focus in mind and make concepts like IOC unnecessary/unwanted. It is a hard shift if you are used to Design by Contract. Something Rails could benefit from, which the core team realized as you can see on latest versions.Frequentative
Follow up question: what's the best way to document an interface in Ruby? A Java keyword interface may not provide all the relevant info, but it does provide an obvious place to put documentation. I've written a class in Ruby that implements (enough of) IO, but I did it by trial and error and wasn't too happy with the process. I've also written multiple implementations of an interface of my own, but documenting which methods are required and what they are supposed to do so that other members of my team could create implementations proved a challenge.Instill
I don't care what 27 people above me thing, you sir, have nailed it.Kraska
The interface construct is indeed only needed to treat different types as the same in statically typed single-inheritance languages (e.g. treat LinkedHashSet and ArrayList both as a Collection), it has pretty much nothing do with Interface as this answer shows. Ruby is not statically typed so there is no need for the construct.Pneumonoultramicroscopicsilicovolcanoconiosis
I read this as "some interfaces make no sense, therefore interfaces are bad. Why would you want to use interfaces?". It doesn't answer the question and frankly just sounds like someone who doesn't understand what interfaces are for and their been benefit.Areta
@Oddman: I'm not sure where you get the idea that I'm saying interfaces are bad. Interfaces are incredibly important. That's why it's such a shame that Java's interface keyword is incredibly bad at capturing the idea of an interface.Gelderland
My apologies, that's just not how it came across :)Areta
Thank you for getting it in the face of blind cargo-culting.Propolis
I think Esailija's answer does a better job of actually answering the question...Archdeacon
Your argument about the invalidity of the List interface by citing a method that performs a remove in a function called "add" is a classic example of a reductio ad absurdum argument. In particular it is possible in any language (ruby included) to write a method that does something different than what is expected. This is not a valid argument against "interface" it's just bad code.Molar
I think often what many developers miss is that code is as much communicating to other developers as it is to the computer. To me the interface keyword is a note to other developers that you're trying to conform to the interface construct as defined. Yes, you CAN do something stupid like remove a list member instead of add it, but that's kind of missing the point, isn't it? I don't find the interface construct useless at all, I find it extremely useful to know what classes I can use for a given pattern. Life rarely has guarantees, software implementations included.Scavenge
You are roughly right in that interface of an object is defined what it can do. Dynamically typed languages often rely purely on duck typing and what's called an Implicit interface implementation, HOWEVER they (or at least not Ruby) do not allow you to force class to conform to an interface. What java has on the other hand is something called explicit interface implementation -- where you have to actually specify that class conforms to an interface. M. Fowler gives a nice comparison martinfowler.com/bliki/ImplicitInterfaceImplementation.htmlTmesis
And example of language with implicit interface implementation which allows you to enforce the types is GO -- although go doesn't have a concept of classes or inheritance. It allows you to do kind of a "strictly typed ducktyping" if you will.Tmesis
Last but not least. What you said about interfaces in Java is imprecise. Marker interfaces (like Serializable and Clonable) are in the language for historical reason and were replaced by annotations. You are also wrong about the Set example -- there is a formal definition of what operations must a data structure support in order to be a set, the interface in java defines all of them. And about the List#add you are also not-so-correct since you have the concepts of interfaces and method contracts mixed together. Even the implicit interfaces in Ruby wont allow you to ensure method contracts.Tmesis
In response to "What is the Interface of java.util.List<E>.add?", you covered the behavioral contract well, but you missed 3 things: required methods introduced by interface composition, method signatures, and an error specification. I'll omit the required method specification for brevity. The method signatures are 1) add() should return nothing 2) add() should accept two positional parameters, an int and an E (matching the concrete type declaration). The error specification ensures child classes will likewise require clients to handle behavioral errors appropriate to the interface.Alt
It would have been better to say "Ruby does not have interfaces at all, live with it!"Undefined
After reading through the answer, I'm still left wondering, "How does Ruby do dynamic dispatch?" C++ has the virtual keyword, how does Ruby do it?Labial
@MooingDuck: get the object, get its class pointer, check the method table if there is a method corresponding to the message. If yes, execute it and you are done. If not, get the superclass pointer and go to step #2. Repeat until the superclass pointer is nil. Restart the process with the message method_missing, the original message as the first argument and the original arguments as the following arguments. If this also fails, raise a NoMethodError. This is slightly complicated by Module#prepend and Refinements, but only slightly.Gelderland
@JörgWMittag That sounds.... unreasonable as a "real" programming language :OLabial
@MooingDuck: That's how Smalltalk, Self, ECMAScript, Python, Ruby, PHP, Java, C#, and practically any object-oriented language works. But of course, there is no universally agreed-upon definition of what constitutes a "real" programming language, so you might be right. You can write large, complex, highly-performant, highly-reliable systems in all of those languages, that's "real" enough for me.Gelderland
@JörgWMittag: I was really asking about Syntax. Most languages have explicit interfaces, where you can pass around those interfaces, and call the methods on the interface as if they were full classes, and the compiler figures out the dynamic dispatch for you. Your prior comment implies that in Ruby, one has to manually write all the code to check for superclasses, since there is no syntactic concept of an interface. Is that the case, or did I misunderstand? I feel like I misunderstand because that would also imply no inheritance, in which case, checking for superclasses makes no sense.Labial
@MooingDuck: "I was really asking about Syntax." – The syntax for virtual dispatch in Ruby is foo.bar(), which means "send the message bar to the result of evaluating the expression foo". Again, this is no different from pretty much every other object-oriented programming language (except languages in the Smalltalk family, where the same thing would be written as foo bar).Gelderland
@JörgWMittag: That's only half of dynamic dispatch. The other half is: how do you erase the implementation to leave only the interface/superclass? In other languages, this is implicit. In Ruby, is it also? I can make a list of superclasses, and when I call a method of a contained item, then the method of the derived class is called? In that case, an interface is merely a superclass, same as C++.Labial
K
89

Ruby 3 (2021)

Ruby 3.0 introduced a type system called RBS, which supports interfaces.

interface _IntegerConvertible
   def to_int: () -> Integer
end

Source: https://blog.appsignal.com/2021/01/27/rbs-the-new-ruby-3-typing-language-in-action.html

Sorbet (2020)

Stripe built a static type checker called Sorbet, which supports interfaces. See Abstract Classes and Interfaces in the sorbet docs.

RSpec (Original answer, 2012)

Try rspec's "shared examples":

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

You write a spec for your interface and then put one line in each implementer's spec, eg.

it_behaves_like "my interface"

Complete example:

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
Kishakishinev answered 22/3, 2012 at 16:46 Comment(4)
I do believe that this should be the accepted answer. This is the way most type weak languages can provide Java like interfaces. The accepted one explains why Ruby does not have interfaces, not how to emulate them.Provinciality
I agree, this answer helped me much more as a Java developer moving to Ruby than above accepted answer.Exospore
Yeah but the whole point of an interface is that it has the same method names but the concrete classes have to be the ones to implement the behavior, which presumably is different. So what am I supposed to be testing in the shared example?Ineffable
Ruby makes everything pragmatic. If you would like to have documented and well written code, add tests/specs and that will be kind of a static typing check.Fructification
T
86

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 interfaces mean very different things, yet they have the exact same signature.

So, if two interfaces that mean different things, have the same signature, what exactly is the interface even guaranteeing you?

Another good example:

package java.util;

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

What is the Interface of java.util.List<E>.add?

  • that the length of the collection does not decrease
  • that all the items that were in the collection before are still there
  • that element 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<E> implements java.util.List<E> {
    void add(int index, E element)
        throws UnsupportedOperationException, ClassCastException,
            NullPointerException, IllegalArgumentException,
            IndexOutOfBoundsException {
        remove(element);
    }
}

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 it's 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 it 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.

Thistle answered 14/12, 2010 at 13:38 Comment(23)
While a good read I don't find the answer that helpful. It reads like a dissertation on why interface is useless, missing the point of its use. It would have been easier to say that ruby is dynamically typed and that has a different focus in mind and make concepts like IOC unnecessary/unwanted. It is a hard shift if you are used to Design by Contract. Something Rails could benefit from, which the core team realized as you can see on latest versions.Frequentative
Follow up question: what's the best way to document an interface in Ruby? A Java keyword interface may not provide all the relevant info, but it does provide an obvious place to put documentation. I've written a class in Ruby that implements (enough of) IO, but I did it by trial and error and wasn't too happy with the process. I've also written multiple implementations of an interface of my own, but documenting which methods are required and what they are supposed to do so that other members of my team could create implementations proved a challenge.Instill
I don't care what 27 people above me thing, you sir, have nailed it.Kraska
The interface construct is indeed only needed to treat different types as the same in statically typed single-inheritance languages (e.g. treat LinkedHashSet and ArrayList both as a Collection), it has pretty much nothing do with Interface as this answer shows. Ruby is not statically typed so there is no need for the construct.Pneumonoultramicroscopicsilicovolcanoconiosis
I read this as "some interfaces make no sense, therefore interfaces are bad. Why would you want to use interfaces?". It doesn't answer the question and frankly just sounds like someone who doesn't understand what interfaces are for and their been benefit.Areta
@Oddman: I'm not sure where you get the idea that I'm saying interfaces are bad. Interfaces are incredibly important. That's why it's such a shame that Java's interface keyword is incredibly bad at capturing the idea of an interface.Gelderland
My apologies, that's just not how it came across :)Areta
Thank you for getting it in the face of blind cargo-culting.Propolis
I think Esailija's answer does a better job of actually answering the question...Archdeacon
Your argument about the invalidity of the List interface by citing a method that performs a remove in a function called "add" is a classic example of a reductio ad absurdum argument. In particular it is possible in any language (ruby included) to write a method that does something different than what is expected. This is not a valid argument against "interface" it's just bad code.Molar
I think often what many developers miss is that code is as much communicating to other developers as it is to the computer. To me the interface keyword is a note to other developers that you're trying to conform to the interface construct as defined. Yes, you CAN do something stupid like remove a list member instead of add it, but that's kind of missing the point, isn't it? I don't find the interface construct useless at all, I find it extremely useful to know what classes I can use for a given pattern. Life rarely has guarantees, software implementations included.Scavenge
You are roughly right in that interface of an object is defined what it can do. Dynamically typed languages often rely purely on duck typing and what's called an Implicit interface implementation, HOWEVER they (or at least not Ruby) do not allow you to force class to conform to an interface. What java has on the other hand is something called explicit interface implementation -- where you have to actually specify that class conforms to an interface. M. Fowler gives a nice comparison martinfowler.com/bliki/ImplicitInterfaceImplementation.htmlTmesis
And example of language with implicit interface implementation which allows you to enforce the types is GO -- although go doesn't have a concept of classes or inheritance. It allows you to do kind of a "strictly typed ducktyping" if you will.Tmesis
Last but not least. What you said about interfaces in Java is imprecise. Marker interfaces (like Serializable and Clonable) are in the language for historical reason and were replaced by annotations. You are also wrong about the Set example -- there is a formal definition of what operations must a data structure support in order to be a set, the interface in java defines all of them. And about the List#add you are also not-so-correct since you have the concepts of interfaces and method contracts mixed together. Even the implicit interfaces in Ruby wont allow you to ensure method contracts.Tmesis
In response to "What is the Interface of java.util.List<E>.add?", you covered the behavioral contract well, but you missed 3 things: required methods introduced by interface composition, method signatures, and an error specification. I'll omit the required method specification for brevity. The method signatures are 1) add() should return nothing 2) add() should accept two positional parameters, an int and an E (matching the concrete type declaration). The error specification ensures child classes will likewise require clients to handle behavioral errors appropriate to the interface.Alt
It would have been better to say "Ruby does not have interfaces at all, live with it!"Undefined
After reading through the answer, I'm still left wondering, "How does Ruby do dynamic dispatch?" C++ has the virtual keyword, how does Ruby do it?Labial
@MooingDuck: get the object, get its class pointer, check the method table if there is a method corresponding to the message. If yes, execute it and you are done. If not, get the superclass pointer and go to step #2. Repeat until the superclass pointer is nil. Restart the process with the message method_missing, the original message as the first argument and the original arguments as the following arguments. If this also fails, raise a NoMethodError. This is slightly complicated by Module#prepend and Refinements, but only slightly.Gelderland
@JörgWMittag That sounds.... unreasonable as a "real" programming language :OLabial
@MooingDuck: That's how Smalltalk, Self, ECMAScript, Python, Ruby, PHP, Java, C#, and practically any object-oriented language works. But of course, there is no universally agreed-upon definition of what constitutes a "real" programming language, so you might be right. You can write large, complex, highly-performant, highly-reliable systems in all of those languages, that's "real" enough for me.Gelderland
@JörgWMittag: I was really asking about Syntax. Most languages have explicit interfaces, where you can pass around those interfaces, and call the methods on the interface as if they were full classes, and the compiler figures out the dynamic dispatch for you. Your prior comment implies that in Ruby, one has to manually write all the code to check for superclasses, since there is no syntactic concept of an interface. Is that the case, or did I misunderstand? I feel like I misunderstand because that would also imply no inheritance, in which case, checking for superclasses makes no sense.Labial
@MooingDuck: "I was really asking about Syntax." – The syntax for virtual dispatch in Ruby is foo.bar(), which means "send the message bar to the result of evaluating the expression foo". Again, this is no different from pretty much every other object-oriented programming language (except languages in the Smalltalk family, where the same thing would be written as foo bar).Gelderland
@JörgWMittag: That's only half of dynamic dispatch. The other half is: how do you erase the implementation to leave only the interface/superclass? In other languages, this is implicit. In Ruby, is it also? I can make a list of superclasses, and when I call a method of a contained item, then the method of the derived class is called? In that case, an interface is merely a superclass, same as C++.Labial
H
51

Can we expose interfaces in Ruby like we do in java and enforce the Ruby modules or classes to implement the methods defined by interface.

Ruby does not have that functionality. In principle, it does not need them as Ruby uses what is called duck typing.

There are few approaches you can take.

Write implementations that raise exceptions; if a subclass attempts to use the unimplemented method, it will fail

class CollectionInterface
  def add(something)
    raise 'not implemented'
  end
end

Along with above, you should write testing code that enforces your contracts (what other post here incorrectly call Interface)

If you find yourself writing void methods like above all the time, then write a helper module that captures that

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

Now, combine the above with Ruby modules and you are close to what you want...

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

And then you can do

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

Let me emphasise once again: this is a rudimentary, as everything in Ruby happens at runtime; there is no compile time checking. If you couple this with testing, then you should be able to pick up errors. Even further, if you take the above further, you could probably be able to write an Interface that performs checking on the class first time an object of that class is created; making your tests as simple as calling MyCollection.new... yeah, over the top :)

Herriott answered 16/7, 2014 at 7:9 Comment(2)
Ok but if your Collection = MyCollection implement a method no defined in the Interface this works perfectly, so you can't ensure your Object have only the Interface methods définitions.Saberhagen
This is pretty awesome, thanks. Duck typing is fine, but sometimes its good to explicitly communicate to other devs how an interface should behave.Johanna
N
11

As everyone here said, there is no interface system for ruby. But through introspection, you can implement it yourself quite easily. Here is a simple example that can be improved in many ways to help you get started:

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

Removing one of the methods declared on Person or change it number of arguments will raise a NotImplementedError.

Nowhere answered 16/10, 2013 at 14:40 Comment(0)
C
6

As many answers indicate, there is no way in Ruby to force a class to implement a specific method, by inheriting from a class, including a module or anything similar. The reason for that is probably the prevalence of TDD in Ruby community, which is a different way of defining the interface - the tests not only specify the signatures of the methods, but also the behavior. Thus if you want to implement a different class, that implements some already defined interface, you have to make sure that all the tests pass.

Usually the tests are defined in isolation using mocks and stubs. But there are also tools like Bogus, allowing for defining contract tests. Such tests not only define the behavior of the "primary" class, but also check that the stubbed methods exist in the cooperating classes.

If you are really concerned with interfaces in Ruby I would recommend using a testing framework that implements contract testing.

Cimbalom answered 15/5, 2015 at 20:20 Comment(0)
Z
5

There are no such things as interfaces in the Java way. But there are other things you can enjoy in ruby.

If you want to implement some kind of types and interface - so that the objects can be checked whether they has some methods/messages you require from them -, you can then take a look at rubycontracts. It defines a mechanism similar to the PyProtocols. A blog about type checking in ruby is here.

The mentioned approached are not living projects, although the goal seems to be nice at first, it seems that most of the ruby developers can live without strict type checking. But the flexibility of ruby enables to implement type checking.

If you want to extend objects or classes (the same thing in ruby) by certain behaviors or somewhat have the ruby way of multiple inheritance, use the include or extend mechanism. With include you can include methods from another class or module into an object. With extend you can add behavior to a class, so that its instances will have the added methods. That was a very short explanation though.

I my opinion the best way to resolve the Java interface need is to understand the ruby object model (see Dave Thomas lectures for instance). Probably you will forget about Java interfaces. Or you have an exceptional application on your schedule.

Zoniazoning answered 14/12, 2010 at 14:8 Comment(1)
Those Dave Thomas lectures are behind a paywall.Turbojet
S
3

All examples here are interesting but missing the validation of the Interface contract, I mean if you want your object implement all Interface methods definition and only this ones you can't. So I propose you a quick simple example (can be improved for sure) for ensure you have exactly what you expect to have through your Interface (The contract).

consider your Interface with the defined methods like that

class FooInterface
  class NotDefinedMethod < StandardError; end
  REQUIRED_METHODS = %i(foo).freeze
  def initialize(object)
    @object = object
    ensure_method_are_defined!
  end
  def method_missing(method, *args, &block)
    ensure_asking_for_defined_method!(method)
    @object.public_send(method, *args, &block)
  end
  private
  def ensure_method_are_defined!
    REQUIRED_METHODS.each do |method|
      if [email protected]_to?(method)
        raise NotImplementedError, "#{@object.class} must implement the method #{method}"
      end
    end
  end
  def ensure_asking_for_defined_method!(method)
    unless REQUIRED_METHODS.include?(method)
      raise NotDefinedMethod, "#{method} doesn't belong to Interface definition"
    end
  end
end

Then you can write a object with at least the Interface contract:

class FooImplementation
  def foo
    puts('foo')
  end
  def bar
    puts('bar')
  end
end

You can call your Object safely through your Interface for ensuring you are exactly what the Interface define

#  > FooInterface.new(FooImplementation.new).foo
# => foo

#  > FooInterface.new(FooImplementation.new).bar
# => FooInterface::NotDefinedMethod: bar doesn't belong to Interface definition

And you can as well ensure your Object implement all your Interface methods definition

class BadFooImplementation
end

#  > FooInterface.new(BadFooImplementation.new)
# => NotImplementedError: BadFooImplementation must implement the method foo
Saberhagen answered 28/5, 2015 at 15:4 Comment(0)
P
2

I've extended a bit on carlosayam's answer for my additional needs. This adds a couple additional enforcements and options to the Interface class: required_variable and optional_variable which supports a default value.

I'm not sure that you would want to use this meta programming with something too large.

As other answers have stated, you're best off writing tests that properly enforce what you're looking for, especially once you want to start enforcing parameters and return values.

Caveat this method only throws an error upon the calling of the code. Tests would still be required for proper enforcement before runtime.

Code Example

interface.rb

module Interface
  def method(name)
    define_method(name) do
      raise "Interface method #{name} not implemented"
    end
  end

  def required_variable(name)
    define_method(name) do
      sub_class_var = instance_variable_get("@#{name}")
      throw "@#{name} must be defined" unless sub_class_var
      sub_class_var
    end
  end

  def optional_variable(name, default)
    define_method(name) do
      instance_variable_get("@#{name}") || default
    end
  end
end

plugin.rb

I used the singleton library for the given pattern I am utilizing. This way any subclasses inherit the singleton library when implementing this "interface".

require 'singleton'

class Plugin
  include Singleton

  class << self
    extend Interface

    required_variable(:name)
    required_variable(:description)
    optional_variable(:safe, false)
    optional_variable(:dependencies, [])

    method :run
  end
end

my_plugin.rb

For my needs this requires that the class implementing the "interface" subclasses it.

class MyPlugin < Plugin

  @name = 'My Plugin'
  @description = 'I am a plugin'
  @safe = true

  def self.run
    puts 'Do Stuff™'
  end
end
Picofarad answered 27/12, 2017 at 19:6 Comment(0)
W
2

Ruby itself has no exact equivalent to interfaces in Java.

However, since such an interface can sometimes be very useful, I developed a gem for Ruby myself, which emulates Java interfaces in a very simple way.

It's called class_interface.

It works quite simply. First install the gem by gem install class_interface or add it to your Gemfile and rund bundle install.

Defining an interface:

require 'class_interface'

class IExample
  MIN_AGE = Integer
  DEFAULT_ENV = String
  SOME_CONSTANT = nil

  def self.some_static_method
  end

  def some_instance_method
  end
end

Implementing that interface:

class MyImplementation
  MIN_AGE = 21
  DEFAULT_ENV = 'dev' 
  SOME_CONSTANT = 'some_value'

  def specific_method
    puts "very specific"
  end

  def self.some_static_method
    puts "static method is implemented!"
  end

  def some_instance_method
    # implementation
  end

  def self.another_methods
    # implementation
  end

  implements IExample
end

If you don't implement a certain constant or method or the parameter number doesn't match, a corresponding error will be raised before the Ruby program is executed. You can even determine the type of the constants by assigning a type in the interface. If nil, any type is allowed.

The "implements" method must be called at the last line of a class, because that is the code position where the implemented methods above are checked already.

More at: https://github.com/magynhard/class_interface

Wynne answered 14/3, 2019 at 20:9 Comment(0)
S
0

I realised that I was using the pattern "Not implemented error" too much for safety checks on objects that I wanted a specific behaviour. Ended up writing a gem that basically allows to use an interface like this:

require 'playable' 

class Instrument 
  implements Playable
end

Instrument.new #will throw: Interface::Error::NotImplementedError: Expected Instrument to implement play for interface Playable

It does not check for method arguments. It does as of version 0.2.0. More detailed example at https://github.com/bluegod/rint

Salomone answered 13/11, 2015 at 19:26 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.