Friday, July 05, 2013

Interface Segregation Principle in Ruby

I was recently asked whether Ruby had a way of honoring the Interface Segregation Principle (ISP). ISP says that a client of an API should only depend on the methods it needs/cares about. In languages like Java, ISP is easy to manage with static typing and judicious use of Interfaces. Ruby doesn't have the same thing, though. Consider the following example:

Let's say that you want to put the controls to fly and land a drone in the hands of someone with Secret clearance, but restrict access to operations like taking pictures and launching missiles to a client with a different security clearance. The problem with the Drone class is that the methods for doing all of the operations are visible to anyone with a reference to a Drone instance. ISP says that the lower-level clearance client should only be able to see a subset of all of the methods on a Drone instance. We can't control this on the instance itself very easily because the class is defined to expose all methods to anyone with a reference to an instance of it.

What you can do in Ruby is present access to an object via a proxy to it using the Forwardable module. In the example below, the Pilotable class tells Forwardable where to send invocations of the methods "take_off", "land", and "fly_to". Any other method invocation will blow up. An object of Pilotable class doesn't expose the reference to an aircraft it is wrapping, instead it simply provides a means of invoking a subset of its methods.

This is the essence of the Interface Segregation Principle: provide a client with an interface limited only to the methods needed in the context of the client's interaction.


Anonymous said...

Simple Great!

Bruno Sapienza said...

What are your thoughts regarding this approach VS doing this with the decorator pattern?

Shahriyar Nasir said...

@Bruno Sapienza I feel that a decorator pattern is too heavy for this since that would delegate calls to all methods on the decorator object to the decorated object. The design win that decorator gives us is flexibility to add new capabilities based on runtime context. Here, the proxy pattern is more appropriate because we need to restrict the API of the aircraft depending on different security clearances. We don't need to add additional capabilities. Just restrict access to existing ones.