Making Interfaces in Ruby
Interfaces are a universal concept across several OOP style languages. PHP definitely has them too:
2 things to know about interfaces:
- They are not able to live on their own. They’re kind of like the vines of OOP, they require a instantiable thing to exist.
- You can’t actually define functionality inside of the methods. They just enforce the public facing contract of a class.
So if we tried to instaniate an Interface in PHP we’re setup to have a bad time:
In order for us to use an Interface, we need to create a regular class that inherits the Interface:
What happens when we attempt to assign an Interface to a class that doesn’t implement the same signature?
Why did this break?
By implementing the Animal
interface on a Cactus
which doesn’t have a eat
method, we broke the contract that the Animal
interface guarantees.
Animal
is a great example of an interfact because there is no prototypical “Animal” instance. The term Animal by itself doesn’t specify a specific red panda, lion, or crawdad. But immediately you know that this instance is not a plant.
We’ll get into the power of Interfaces combined with Type Hinting and Ruby’s equivalent in later chapters.
Making an Abstract Class in Ruby
I’m not just going to give you an example right off the bat. Cmon what’s the fun in that.
Let’s just take this to a higher level. Why does the Interface feature exist in PHP? Well, it’s a method of guaranteeing that a particular class implements some kind of signature.
When you have Cat
or Armadillo
inherit from Animal
, you have the peace of mind knowing that any instance implementing Animal
is going to have a public eat()
method.
So, I have to apologize. I am so so so sorry. I lied to you again. But the truth is there is no such thing as an interface in Ruby.
This came as a shock to me when I first started learning the language. I couldn’t believe that had OOP paradigms so embedded in it’s core but it didn’t have such Interfaces!
After a few months I’ve started to realize why.
Think about the goal of an Interface and how close we can get with just the basic building blocks OOP has to offer.
Behold the official and enterprise Interface implementation in Ruby:
class Animal
def eat
raise 'Don\'t forget to implement the eat method'
end
end
I’m not kidding. Look what happens when we attempt to call eat
on a Cactus
:
class Cactus < Animal
def drink(water)
puts "gulp gulp #{water}"
end
end
cactus = Cactus.new
cactus.eat
=> StandardError: 'Don\'t forget to implement the eat method'
So unlike PHP, the class won’t immediately break because eat
is missing. That’s absolutely true. In PHP and other strongly typed languages, the compiler catches these lapses in judgement.
Because Ruby is such a dynamic language, we place our trust in tests. But after that initial shock of missing the “formal safety” of concepts like Interfaces give you - you don’t miss them.
Ruby reduces the number of built in OOP concepts. However it doesn’t limit you from implementing them. Think about it, which do you trust more? Your contractually bound classes or your automated tests?
PHP gives us both, the ability to create contracts with the peace of mind of knowing exactly that a public interface exists - also the power of automated tests with PHPUnit and Codeception.
Yes it’s true in Ruby you’ll need to test more to make up for lack of contracts and type hinting. However, it’s because of Ruby’s dynamic nature and flexiblity to bend the rules testing your actually code becomes a blissful experience. Whereas in it’s PHP cousin, if you don’t build with tests in mind, at a certain point your code becomes virtually untestable.