The Art of Uniform Interface
Context
After reading how the first, second, third and so on methods were added in the Rails doctrine by DHH, I started to look for ways on how I would actually go about doing something better. Obviously the drawback of defining methods like that is that they are not scalable. But, I see his point of not indexing into an array to get data for some common use cases.
Challenge
How can we come up with a uniform interface that does not result in explosion of method and at the same time achieves ignorance of indexing into an array? Ruby, has values_at method for an array.
> a = [1,2]
=> [1, 2]
> a.values_at(1)
=> [2]
> a.values_at(0)
=> [1]
This exposes the knowledge of the indexing of an array. It also knows that is an array. Ruby also has values_at method for hash.
h = { "cat" => "feline", "dog" => "canine", "cow" => "bovine" }
h.values_at("cow", "cat")
#=> ["bovine", "feline"]
This exposes the knowledge that is an hash. You need to know the key in the hash to get the corresponding value.
Well Defined Interface
I don't care about the type of data structure, just give me the element in the given position.
class Array
def element(position)
self[position - 1]
end
end
p [4,5,6].element(3)
You can just provide the position as the parameter to element method instead of going through the mental mapping of position and index of an array. Similarly, we can define a element method for Hash.
class Hash
def element(position)
a = self.to_a[position-1]
{a[0] => a[1]}
end
end
h = {a: 1, b: 2, c: 3, d: 4}
p h.element(2)
This returns :
{b: 2}
Both implementation are data structure agnostic. It does not force the developers to map the position of an element and the index in array or knowing the key of a hash.
Principle of Least Surprise
I was surprised when I tried to call element method on Array and Hash and I got errors. Ruby has failed in this case. It only has first and last method defined.
Summary
In this chapter, we saw how we can define a uniform interface that hides the data structure and knowledge required to pass in parameters to a method. This solution eliminates parametric coupling.