26
January
Written by admin.
Posted in: Ruby
First, create the interface as a “mixin” (a Ruby module that will be included into another).
module Sociable
@@must_have = [:smile, :feign_interest, :paraphrase, :laugh_at_bad_jokes]
class << self
def included (b)
@@must_have.each do |m|
raise NotImplementedError,
"'#{b}' must implement the '#{m}' method" unless
b.method_defined? m
end
end
end
end
Here you can see we have a list of methods that must be implemented by the including class in @@must_have. Sociable.include is called automatically whenever another class or module includes Sociable.
Below is a class that is to implement Sociable:
require 'sociable'
class Salesman
include Sociable
end
Now the class Salesman won’t load until it has implemented all of the methods in @@must_have. However there is one gotcha! Let’s say we do implement the methods as such:
require 'sociable'
class Salesman
include Sociable
@@paraphrases = ['Totally','I hear that','Absolutely','Uh-hu']
def smile
puts ":-)"
end
def feign_interest
puts ":-o"
end
def paraphrase
puts @@paraphrases[rand @@paraphrases.length]
end
def laugh_at_bad_jokes
"o(∩_∩)o...哈哈" # Chinese for "haha"
end
end
But oops! When we require ‘salesman’ we get a huge problem:
NotImplementedError: 'Salesperson' must implement the 'smile' method
What happened? As it turns Ruby does not look ahead to see which methods are defined within the module before it makes the actual call to Sociable.include. This requires us to move that include to the bottom of our class… which is kind of ugly. If anyone knows how to avoid this please leave a comment! In any case here is what the Salesman class should look like:
require 'sociable'
class Salesman
@@paraphrases = ['Totally','I hear that','Absolutely','Uh-hu']
def smile
puts ":-)"
end
def feign_interest
puts ":-o"
end
def paraphrase
puts @@paraphrases[rand @@paraphrases.length]
end
def laugh_at_bad_jokes
"o(∩_∩)o...哈哈" # Chinese for "haha"
end
include Sociable
end
So, how do we ensure that a class does indeed conform to an interface? After all, the class may very well not include Sociable at all. Well, we do have a way to check up on this.
Salesman.included_modules.include? Sociable
This line will return true or false and will allow us to exit gracefully if the module we are dealing with doesn’t implement an interface that we expect it to.
Please leave your comments or suggestions for improvements.
Stay in touch with the conversation, subscribe to the RSS feed for comments on this post.