- Read Tutorial
- Watch Guide Video
As we make our way through the SOLID Development journey, it’s time we turned to the I in SOLID, which represents the: interface segregation principle.
Interface Segregation Principle Definition
As with several of the other SOLID design concepts, this represents a scary name for an important topic. A dead simple definition of the interface segregation principle is that code should not be forced to depend on methods that it doesn’t use.
If this is a bit fuzzy, don’t worry I’m going to walk through a code example that clears it up.
Believe it or not, this is one of the easier SOLID concepts to understand and work with.
Interface Segregation Principle Code Example
Let’s start by taking a look at a program that manages a blog.
class Blog def edit_post puts "Post edited" end def delete_post puts "Post removed" end def create_post puts "Post created" end end blog = Blog.new blog.edit_post blog.delete_post blog.create_post
(Obviously this program simply prints out some values in each method). If we run the program you’ll see that this code works properly.
Post edited Post removed Post created
Introducing the Moderator
Now what happens if we want to create a Moderator
class? On a side note: let’s ignore the fact that a Moderator class should never be connected or inherit from a Blog class since a moderator isn’t a type of blog. However in preparing for this guide I walked through a number of examples and this case study illustrated the interface segregation principle the best.
Our Moderator class is very specialized. Moderators should only be able to edit a post. However this poses a problem because if we try to use code like this:
class Blog def edit_post puts "Post edited" end def delete_post puts "Post removed" end def create_post puts "Post created" end end class Moderator < Blog end moderator = Moderator.new moderator.edit_post moderator.delete_post moderator.create_post
The problem with this Moderator class is that it not only can edit posts, but also delete posts. For practical reasons this is bad. However it is also breaking the interface segregation principle because we’ve intertwined our Blog class with our Moderator. And even though Moderator may not directly depend on all of the Blog methods, it’s still coupled with them since they can be called from instances of the class.
A Better Way
Thankfully Ruby offers us a better way of giving us the behavior that we need. By leveraging the Forwardable
module we can limit the scope of what our Moderator class can access.
require 'forwardable' class Blog def edit_post puts "Post edited" end def delete_post puts "Post removed" end def create_post puts "Post created" end end class Moderator extend Forwardable def_delegators :@blog, :edit_post def initialize(blog) @blog = blog end end moderator = Moderator.new(Blog.new) moderator.edit_post moderator.delete_post
This code starts by importing the Forwardable
module in both the file and inside the Moderator class. Next we update the Moderator class so it no longer inherits from Blog. With those components cleaned up we can leverage the Forwardable module’s def_delegators
method to list what we want the Moderator class to have access to.
In this case we want the Moderator to have access to the Blog class and the edit_post method inside of Blog. Notice how this is different than having Moderator inherit from Blog? Instead of giving access to the full class definition we’re able to pick and choose what elements Moderator should be able to work with.
Next we’ll setup an initialize method that takes a blog as an argument. This is how we’re letting Ruby know that our Moderator class needs to be passed the Blog class as an argument.
Lastly we can instantiate a new Moderator and pass in a new Blog as an argument.
The Result
After running this code you’ll see that our moderator can edit posts successfully.
Post edited solid.rb:28:in `<main>': undefined method `delete_post' for #<Moderator:0x007f7f5189a9e0 @blog=#<Blog:0x007f7f5189aa08>> (NoMethodError)
However when it tries to delete a post it gets an error since we didn’t give it access to that method.
A Caveat
Before ending this guide I want to make one point of clarification. The walk through I just went through is specific to how you can work with the interface segregation principle in the Ruby language. However, since Ruby is a dynamic language it doesn’t require developers to declare class and method types. The interface segregation principle has a number of other requirements for statically typed languages such as Java or c++. So please keep that in mind if you work with those types of languages.
Ruby does quite a bit of the hard work for us when it comes to Object Oriented Development, however this is still an important concept to understand. And as a bonus you were able to learn about the Forwardable module!