Minimal vs. Humane Interfaces
Posted by Skrud at Saturday, December 10th 2005 at 8:20pm
It seems there’s been some debating in the Software Engineering world about whether or not a Minimal Interface is perferable to a Humane Interface. It looks like it all started with Martin Fowler’s discussion of Humane Interfaces, comparing Java’s List interface with Ruby’s Array class. Java’s List interface has a mere 25 methods, whereas Ruby’s Array (which is really a List) has a whopping 78 methods. Many of these are so-called “convenience” methods that do simple things that may be often used:
For example, to receive the first and last elements in an Array you can simply do:
aList.first
aList.lastThis is an example of a Humane Interface, where there are a host of methods that you may need to use, but shouldn’t need to reimplement every time.
Compared with Java, an example of a Minimal Interface:
aList.get(0);
aList.get( aList.size() - 1 );(Arguably, Java’s LinkedList class has a getFirst() and getLast() method, both of which are inaccessible when you are using the comment List interface as a declared type… which may speak to the organisation of Java’s libraries…)
There is an interseting rebuttal by Elliotte Harold, who has written plenty of Java books. It’s no wonder where he stands. This seemed to spawn a lot of discussion, and there’s a great response to Elliotte by James Robertson on his blog. The comments are also a good read and many bring up some intresting points. I also like the comments on Cee’s Blog.
Personally I think the strongest argument in support of a Humane Interface is the fact that it’s intention revealing. That is, if you’re reading someone’s code, even if you’re not familiar with the programming language or its implementation, and see something like aList.first, you’ll probably have a pretty good idea of what it does. Now, what if you didn’t know anything about Lists are implemented using indexing and you see aList.get(0) or aList.get( aList.size() - 1 )? You might have to think about it for a bit, first.
Now that I think about it, that method call is really saying: “Give me the element at the position that is one less than your total size,” which is a really round-a-bout way of doing things. I certainly prefer the simplicity of “Give me your last element”.
I particularly liked Fowler’s comment on using length or size to get the number of elements in a list:
Another interesting consequence of ruby’s humane interface philosophy is the aliasing method names. When you want the length of a list, should you use length or size? Some libraries use one, some the other, Ruby’s Array has both, marked as aliases so that either name calls the same code. The rubyist view being that it’s easier for the library to have both than to ask the users of a library to remember which one it is.
I also agree with Jim Weirich who points out that the Humane Interface approach, even if it might mean that you’ll never use half the methods in a given API, you’ll know where to look when you _do_ need a particular method:
As a result, methods tend to migrate to the classes they belong to, rather than put in a utility method somewhere. Although I might never use
assoc, if I were to look for it, I would start in the Array class, or one of its included modules. In fact, I did need assoc recently. Guess where I found it.
I love thinking about this kind of stuff. Coincidentally, I had a Software Design final exam this morning. :D






Humane is EMACS or Perl. Minimal is Unix, C.
Skrud, who gives a crap?! Go get laid. :D
(Sorry - I do not hold myself accountable in any way, shape or form: it’s 2:30am)
I think I’m a fan of humane interfaces if not solely for the intention revealing point mentioned.
While humane interfaces might suffer from “interface bloat” according to some minimalist fans, I think that a certain amount of effort has to be expended in order to build something; if the builder wants to alleviate pressure on the developer of the tools that the builder’s using, then he’s going to use a minimalist interface.
An analogy would be me reading a Springer text on some mathematical concept. It really does have what I need to know in it, but I better know what I’m getting into, because if I want lots of examples and solved problems (like a Schaum’s), I’m using the wrong tool.
If the builder (or user, whatever) decides to use a tool that balances the effort equation so that more of it falls on the developer of the tools, then they would choose a tool that provides the best humane interface; the best being a tool that has intuitive uses (aptly named function calls and/or classes), and not necessarily just having a plethora of options.
This is all probably obvious, but I just wanted to point out that it’s really a case of where effort should be placed: by the interface designer/implementer, or by the user of the interface and those who have to work with their code.
This same kind of balance of effort can be extended to lots of other stuff, like writing: a crappy writer (someone who didn’t put enough effort into their writing) could have good ideas, but I have to spend extra effort understanding what they’re getting at. Likewise instruction manuals or maps with poorly laid out, but accurate pictures, etc.
In writing this, my girlfriend got to level 10 in the original GB Tetris, but her hands fell apart. I told her that Tetris is NP-hard to make her feel better (of course, brick velocity doesn’t matter in this observation, but whatever).
Oh yeah, I forgot to mention that there’s another definition of humane interfaces (more focussed on GUI implementation, but has philosophical ideas that are widely applicable) by Jef Raskin, who did early work for Apple: http://jef.raskincenter.org/humaneinterface/summaryof_thi.html
I think the interface designer should be the one to place more effort, since his tools are the ones that are going to be used by the application developer. Every step he takes, is one less that everyone else needs to take. So things like
lastElementwon’t keep getting re-implemented in every program that needs it: duplication is bad.If it’s part of the interface, it’s already done.
I think those who say that the ‘humane’ interface vs. minimal interface argument has anything to do with who should have the burden of working harder is completely misleading. Writing a minimal interface involves a lot more thinking than one may think of; infact, as much thinking as it would take to do any sort of factoring.
As you may guess, I am pro minimalist interfaces. Small things are beautiful, and large interfaces may not always make the user’s life easier. Take for example the case described above: Ruby’s Array.first and Array.last. It may be easy to read a code with Array.first, but is it easy to write? You have to know that the word is first and not, say, start, beginning, head etc. You have to know the interface, just as you have to know the language when you speak French – even though some words may be comprehensible for someone who doesn’t speak French but speaks English.
The problem of code duplication remains. But it doesn’t mean that the one writing the library should solve it for you. The library author should just provide the possibility to get the thing done. The user can write his/her own methods, or better yet, his own wrappers.
Wrappers don’t make code dirty – not if you use it wisely. Well, atleast not in an OOP language such as Java or Ruby. Just use the wonderful polymorphism feature of the language – and your code will look as beautiful as it could possibly be.
Ofcourse, all these require everyone to think more when writing software, and less let their ‘instincts’ or ‘intuition’ lead the way. There’s is nothing wrong with this, and I think that in most cases, even with a minimalist interfaces, a good design of software that uses the interface for a particular minimalist library can be very readable for any (non Windows) user.
I’ve also got to chime in with my preference for minimalist interfaces. If the Humane interface doesn’t actually save you any real lines of code (for example, the difference between aList.get(0), and aList.first, then I dont’ see much use for it. Does it REALLY become easier to read? I can’t imagine anyone would have difficulty understanding (aList.Length - 1) compared with “end”. All that these interfaces do is force programmers to add another few hundred “commonly used programming axioms” into their brains, and have to waste cycles linking the interface with what you figure it does.
If there’s an interface that, for example, returns the sqareroot of an integer just by typing something like iam_anintergervariableLOVE_ME.sqrt;, I think it’s gone too far.
Well, then there’s the problem of
aList.get( aList.size() - 1 );when using a linked list with a lot of elements - ifsize()is an O(n) algorithm that counts from 0 to the end of the list, for example, and you have to do this every time, andget()is an O(n) algorithm that counts up to ‘n’ blocks from the start of the list, congratulations: your operation for getting the last element of the list is O(n^2). (This will happen with Java’sVector, I believe - but notArrayList).Of course, any List implementation that doesn’t let you get at a given element in approximately O(1) time is just silly.
The problem is that if you want your program to be able to use any class that implements the
Listinterface, you’ll be limited to the methods _in_ that interface, which do not includelastElement()- even if you implement your own inherited class. I see this as kind of an annoying problem.Then there’s the argument, which I quite agree with, that counting the number of methods is an unfair comparison. Since Ruby’s
Arrayclass is an actual class implementation, and includes methods from the base Object class as well as the Enumerable base class and a few others (Ruby doesn’t have “interfaces”), whereas Java’sListis just an interface. Java’sArrayListclass on the other hand, has more methods than Ruby’s when you include all the interfaces and superclasses that it inherits from.Finally, Ruby doesn’t let you do
25.sqrtby default, but you can easily extend the Integer class:difference between Vector and ArrayList seems to be simply that the ArrayList implements the List interface. Where in fact both are Dynamic Arrays.
any self respecting data structure (whether a list or dynamic array) should keep its size for fast retrieval.
in the STL a list has a begin() and end() iterator methods, which are not available to another data structure (vector i believe) and hence u don’t need to have all structures supporting the same methods. Having all data structures supporting te same interface (say HumanList interface) is misleading. It totally hides the inner workings of the data structure and hence leads to usage of the inappropriate structure.
granted that having a whole bunch of methods is nice for code readability, but often it also leads to redundant code, confusion, and general ikines. i like using a library that has a limited number of methods, that way i can understand all its ins and outs, and know how to use it well. otherwise you have to deal with 100+ methods for an array, there is NO way you’d ever know the advantage and the usage of each, and that’s JUST for arrays :P
> It totally hides the inner workings of the data structure…
Isn’t that exactly what the principle of information hiding is supposed to maintain? Why should you need to know how a given data structure is implemented?
I think the Human Interface is just one level of abstraction higher than the Minimal Interface. It’s one step closer to what you might have thought of in your preliminary design / pseudocode: the idea of getting the first and/or last element.
Of course, you can still write
aList[ aList.length - 1 ], but the point is you have the option of writing something that more clearly states your intentions.I also don’t think you should have memorize all the methods in a particular implementation of a library, regardless of it’s size – how often do you code without an API reference handy, anyway? – but knowing that these methods are there gives you a good idea of where to look when you actually _do_ need to use them.
(This example is driving me nuts. I think we need to look at other, non-Array classes.)
To respond to FiG:
> If the Humane interface doesn’t actually save you any real lines of code…
In this case it actually would, since you’d have to watch out for an
IndexOutOfBoundsExceptionif the list size was zero, so you’d either have to remember to put a conditional statement each time you call the last element, _or_ write a function that returns the last element of a list to do it for you, which would totally funk up your OO program, since you can’t just add methods to the class in question (or even write an inherited class, for that matter) if you’re concerned about implementing a particular interface (since you won’t be able to call your own methods from a memeber declared as aListtype).The internal workings of a data structures help you with efficiency :P
we need this often times, but “simple” libraries have rendered coders to become sloppy and in efficient.
granted for many projects that’s not an issue, but for many others it is.
Ahmed,
Technology is a wonderful thing. It deals with efficiency for us. Computers are becoming ever more capable of handling “inefficient” programs– not that it is a good practice to be inefficient and sloppy with code, but the idea of tweaking the internals of data structures to get optimal performance is slowly being obsoleted by the rise in hardware processing power. Soon enough, looking into the internals of such data structures will be seen with the same pointlessness as the way we currently look at delving into assembly code to optimize our C programs.