We have a failure of Imagination
Maybe Java development does rot the brain - have a look at this screed from Cafe au Lait against Martin Fowler. Martin says the following about Ruby's List class (and the same things could be said about Smalltalk's List class):
The obvious contrast to a minimal interface is that humane interfaces tend to be much larger, and indeed humane interface designers don't worry too much about the interface being big. This isn't to say that classes with humane interfaces need be larger in terms of implementation. The fundamental functionality of the two is often quite similar.
A good way of looking at the difference between humane and minimal interfaces is to compare the list components in Java and Ruby. Java has an interface (java.util.List) which declares 25 instance methods. Ruby has an Array class (which is a list not an array) that has 78 methods. That difference in size is something of a clue of that there's a different style here. Both components offer the basic same service, but Ruby's array includes a lot of additional functionality. This functionality is all relatively small things that can be built on Java's minimal interface.
Seems reasonable to me - one of Martin's examples is that Ruby (and Smalltalk) have #first and #last methods. So with a Ruby collections, you can do this:
aList.last
Whereas in Java, you have to do this:
aList.get(aList.size -1)
The response from Cafe au Lait on this is just stunning:
A 78 method List class is about three times as bad as a 25 method List class, not three times as good. A 12 method List class would be about twice as good. Simplicity is a virtue for both users and implementers. There's simply no reason for 78 methods in a basic List class. In fact, there's no reason for 78 public methods in any class. 78 public methods in one class is a code smell. 78 public methods make a class hard to learn, hard to use, hard to test, and hard to maintain. When a class has 78 public methods, it's time to refactor.
The raw number of methods tells us nothing about whether we need to refactor or not; we would have to actually look at the methods, and see if any of them don't fit. However, that's not his objection; he's very worried about all these methods - he really, really wants his classes to be sparse. For instance:
Fowler likes the first and last methods in Ruby, but list.first() is not significantly simpler than list.get(0). list.last() is perhaps a little simpler than list.get(list.size() - 1) but only because Java stupidly indexes everything from 0 rather than 1. And how often do you actually need to get the first item in the list? Needing the last item in a list is even less common. Normally the reason we have a list in the first place is so we can iterate through it using an Iterator or foreach. More often than not no single element -- first, last, or middle -- is explicitly identified in the code. Java's List class does not lack any of the functionality in Ruby's. Java just factors it out into a few more classes, especially the Collections class, and skips a couple of rarely used "convenience" methods. The result is a simpler, easier-to-understand, easier-to-use, more humane API.
Umm, yeah. It's so much simpler to write extra code every time. I suppose that showing this guy the four search methods in Smalltalk's collection classes (which I'm sure Ruby has as well) - #select: , #detect: , #reject: , #collect: would just make his head explode.
I use #first all the time, btw, and I use #last a fair bit as well. Perhaps Java developers don't simply due to their absence? It can be hard to miss what's not provided.
This isn't the first time I've seen this reaction. If I recall, Java's Object class has something like 11 methods. A base VisualWorks image? Object has 235 mthods. I've seen Java guys recoil in horror over that, ranting about the horrid OO design. Funny how Smalltalkers have gotten by for over 25 years this way.
There seems to be a general desire for sparseness in Java-ville. It's a value that neither Smalltalkers nor Rubyists seem to worry over. We are far more interested in solving application problems than we are in the production of extra code to support sparseness.
Update: More from Cees de Groot. And make sure to follow the comments link for more.
Update 2: More here.





Comments
[isomer] December 6, 2005 10:04:07.334
The great thing about aList.first() and aList.last() is that they are intention revealing methods. A user of a List treats it as a black box, and simply ask for the first or last element. The problem with methods like aList.get(0) and aList.get(aList.size() - 1) is that they are implementation revealing. You have to know how the List is implemented to get it to do useful work.
As a rule, the Java class library (which I have become altogether too familiar with) is implementation revealing. You are forever asking objects for their parts (get methods, anyone?) and passing those parts to other objects to do the work. This is the antithesis of Object Oriented programming.
The correct way to design a class is to write intention revealing methods so that a user of the class can ask it to do useful work without knowing anything at all about how it is implemented. From the outside, the class is a black box.
The cell phone is a great mental model for how to design a class. You just punch in the number of the person you want to speak to, and the phone takes care of the rest. You don't need to know anything at all about how the cell network operates, how it interfaces to the land-line system, or anything else for that matter, to complete your call. The phone is a black box with an intention revealing API.
Alan Kay has lamented publicly on more than one occasion that he should have called it Message Oriented programming to drive home the idea that designing the correct messages is of paramount importance.
code duplication
[keith ray] December 6, 2005 10:24:08.423
You just know that because Java's List class doesn't have a "last" method, that everyone who needs to access the last element is going to have to write "alist.get( alist.size() - 1 ) WHICH IS DUPLICATED CODE.
Duplicated code is a sign of bad design, and leads to bugs. There's probably been a million bugs where someone coded alist.get( alist.size() ) attempting to get the last element (which will instead throw an exception), and no doubt less that 80% of those bugs have been fixed.
It wouldn't be so bad if Java programmers could extend existing classes.
I was just wanting Collection.First in .NET
[ Troy Brumley] December 6, 2005 12:19:01.764
Comment by Troy Brumley
I keep coding
CType(myCollection.Item(1), MyType).blahin my .NET application. It's going to drive me to write my own collections. Over on my blog I've also complained about the whole lack of our block/message idiom and how it inflates code size when compared to Smalltalk.Oh, and Amen to Kieth re extending classes. I sometimes wonder if Decorator is so popular because you can't subclass and/or extend most base classes in the (ahem) more mainstream curly-brace world?
size -1
[murphee ( http://www.jroller.com/page/murphee )] December 6, 2005 13:24:43.365
Actually...
x = list.get( list.size() -1 )
won't give you much joy if list can be empty, so the full version of list.last would have to be something like:
if(list.size() > 0){
x = list.get( list.size() -1 )
}
unless you like to get IndexOutOfBounds exceptions.
The point is...
[ James Robertson] December 6, 2005 13:57:09.734
Comment by James Robertson
If List had a last() method (as it does in Smalltalk and Ruby), it wouldn't need to be implemented (possibly badly) by every developer that needs it.
more
[seasider] December 6, 2005 14:06:48.274
The funny thing is, LinkedList has a #first and a #last method. That means the code is there but most developers can't use it, because it's not in the List interface. This is really a sign of good design.
But there's more wrong with the collections in Java: optional methods Which means the implementation may or may not support the method. This is one of the benefits of static typing. You know at compiletime that the instance will have the method.
[Vincent Foley] December 6, 2005 14:46:42.802
I couldn't quite explain why Martin Fowler was right and Cafe au Lait was wrong, but isomer said it quite well: intention vs implementation. And we have a perfect example: Ruby's array are 0-indexed and Smalltalk's are 1-indexed. So to access the first element in Ruby, you do arr0, but in Smalltalk it arr at: 1. Having a first (and last) method makes sure that you don't need to remember that, which is great, especially if you switch between languages often.
Intention vs implementaion
[ Terry] December 6, 2005 15:06:41.554
Comment by Terry
I consider intention programming to be critical for good st code. That is also why I don't like enforced type specifications. They tend to make you think implementation and not intention.
I know...
[murphee ( http://www.jroller.com/page/murphee )] December 6, 2005 15:24:51.093
@James
I wasn't arguing your point, just showing that lack of a last() method isn't just tedious but can also generate runtime problems if the access code isn't carefully implemented.
Not really 78
[riffraff] December 6, 2005 16:51:38.252
well, this is just a note about ruby and not for Smalltalk, but it should be noted that 1/3 of those methods in the ruby Array class are taken "for free" from the Enumerable mixin.
Since Java 1.0 firstElement() and lastElement()
[Isaac Gouy] December 6, 2005 18:58:55.307
I use #first all the time, btw, and I use #last a fair bit as well. Perhaps Java developers don't simply due to their absence? It can be hard to miss what's not provided.
Since Java 1.0 they've had java.util.Vector firstElement() and lastElement().
Missing the topic
[James Robertson] December 6, 2005 19:21:17.816
Isaac, we aren't discussing the (obsolete) Vector class. The class in question is java.util.List.
Don't you know Java?
[Isaac Gouy] December 6, 2005 19:42:18.687
James we aren't discussing the (obsolete) Vector class
There is no java.util.List class - it's an interface.
Far from being obsolete, here's the Java 5 Vector class
Far from obsolete?
[ James Robertson] December 6, 2005 21:39:43.802
Comment by James Robertson
Dunno Isaac, I have plenty of friends developing in Java, and class vector mostly makes them swear. They use the new(ish) Collection libraries
Smalltalk method categories
[Kyle Cordes] December 6, 2005 22:35:53.714
From my highly limited experience with Smalltalk, I will suggest that two reasons why many-methods-per-class is more palatable in Smalltalk than Java are: 1) Smalltalk has a less syntacic overhead per method 2) Smalltalk (at least what I've seen) has "method categories", a tool for organizing methods, if you have 100 of something it is much easier to handle that with some categories, that in one long list.
Even if the hand-cuffs are shiny and new...
[Mel Riffe] December 7, 2005 2:05:26.704
I love posts and comments like this one
On my own weird tangent: Has anyone stopped to think why we (as in Java developers) spend more time dealing with the Problem Space when Smalltalkers and Rubyists spend more time dealing with the Solution Space?
I mean, I spend more time building up my java app before I spend time solving the problem. I feel James' last line in the post sums it up fairly well:
Doesn't that strike you as odd that Java developers spend more time writing extra code to get all those spase objects working together. Why are they afraid of giving their objects some intelligence, some meat, some girth? I'll never know - I've started a Ruby on Rails project and, so far, loving every single minute of it.
-- Mel
Extensions
[Runar Jordahl] December 7, 2005 3:19:21.611
"From my highly limited experience with Smalltalk, I will suggest that two reasons why many-methods-per-class is more palatable in Smalltalk than Java are: 1) Smalltalk has a less syntacic overhead per method 2) Smalltalk (at least what I've seen) has "method categories", a tool for organizing methods"
I'll add a third technique used in Smalltalk for organizing code, which the Java and .Net world miss: Extensions. In many Smalltalk implementations, the ability to divide the methods into multiple packages means you can navigate methods by which layer of the system the method supports. For example, methods on domain objects that answer GUI-related information will (hopefully) be stored in a GUI package. The same applies for methods used for testing, debugging, persistency and so on.
When browsing classes, it is easy to filter methods by packages.
In Smalltalk, a set of classes does not necessarily define a subsystem/component. Instead, a subsystem can add behaviour across classes.
Vector be gone...
[murphee ( http://www.jroller.com/page/murphee )] December 7, 2005 8:01:59.945
@Isaac
It doesn't matter that Vector is still in there, but it is recommended that it's not used anymore, mostly because all its methods are synchronized. It implements java.util.List to fit into the Collections API, but again: it's recommended not to use it. The fact that it's still used by Swing etc, just means that Sun hasn't gotten around to changing that.
But it's curious to see that Vector had the first/lastElement methods, whereas they were removed from the Collections API... I assume the change of guard at the Java team at Sun is the reason here.
Apples to Oranges
[Mike Glenn] December 7, 2005 10:52:24.272
java.util.LinkList provides all the funtionallity your looking for in java without the overhead of synchronization. java.util.List as was pointed out is an interface not a class, it has no implementation. All it does it define what methods must be present for what most computer science course define as a list. Those definitions do not include first/last fuctionality. First/Last are convienence methods. And Java has for a long while provided them in at least two implementations of the List interface, Vector and LinkedList.
Now if you want to compair library size and complexity of java/c# with Ruby/Smalltalk, there's a good discussion there and many have talked about it.
interface vs class
[ Troy Brumley] December 7, 2005 11:35:08.630
Comment by Troy Brumley
I'm still getting used to using the idea of interfaces myself. I'm not entirely sure I like them, but they don't seem as evil as I thought they'd be when I came over from Smalltalk. Still, the distinction isn't obvious to the full time Smalltalker who isn't also coding in Java or .NET. Bear with us over in Smalltalk land :)
java.util.List
[Ravi V] December 7, 2005 12:43:00.266
Mike, I use ArrayList as an implementation of the List interface. Could you please tell me how the presence of "last" in LinkedList helps me? Had a last method ben defined in the List interface itself, and implemented in all its sub-classes, then it would have been easier to use "last" for all List classes. All it shows is that the developers who wrote the LinkedList class made other developers' task a little bit easier.
recommended that it's not used anymore
[Isaac Gouy] December 7, 2005 13:24:38.759
murphee wrote It doesn't matter that Vector is still in there, but it is recommended that it's not used anymore
This argument is an appeal to authority "it is recommended" without saying who the authority is, or showing where they say it.
it's curious to see that Vector had the first/lastElement methods
I agree - it would be interesting to hear, from those who designed the List interface, why they chose not to include first/last.
OK...
[murphee ( http://www.jroller.com/page/murphee )] December 7, 2005 14:26:14.671
@Isaac
This argument is an appeal to authority "it is recommended" without saying who the authority is, or showing where they say it.
OK, you're right. Let me state my reason for the recommendation "Don't use Vector and Hashtable": All methods on these container classes are synchronized which causes synchronization overhead even if the object is never accessed from different threads (I know...modern VMs could get rid of that overhead, but that's not quite here yet and only on those with modern dynamic compilers). If you need synchronized access, then you can synchronize an instance by using Collections.synchronized*.
With all this, and more modern additions (like new Collection classes from the java.utils.concurrency.*, eg. CopyOnWriteList), there isn't really much reason for using Vector. So my recommendation: if you need a List, use ArrayList. If several threads access it: look at appropiate options like the ones mentioned above. And only then, if you decide that really Vector is the best choice, Then use Vector.
Is this an acceptable explanation? (You don't have to agree with it, but that's what I state as reasons).
Sure!
[Isaac Gouy] December 7, 2005 15:13:13.364
Is this an acceptable explanation?
Sure! You've made it clear that this is your viewpoint, and not some kind of official recommendation.
And what about common idioms?
[Brian Slesinsky] December 7, 2005 21:19:07.565
But it's not just one person's opinion - this is also a common practice. Supplying the justification is helpful, but why is it so wrong to tell people what common practice is among Java developers?
[Mike] December 7, 2005 21:28:39.678
"All it does it define what methods must be present for what most computer science course define as a list" It sounds like there should be 2 interfaces, MinimalList and ExtendedList. MinimalList has the minimal set of list functionality, ExtendedList extends MinimalList and has all the "convienence" methods that can be accomplished using the MinimalList functionality, and a class ListExtender should implement ExtendedList and wrap a MinimalList, adding the Extended methods.
Mu
[John D. Mitchell] December 8, 2005 0:23:21.557
There's just too much fighting the same sorts of extremist blah blah blah. I came at this debate from the domain specific language point of view in: Humane interfaces, simplisticity, and domain languages.
Consider AOP's role in creating humane interfaces
[Ramnivas Laddad] December 8, 2005 0:31:08.525
AOP's static crosscutting features can help in transforming a minimalistic interface into a humane interface. In fact, you can get a organization feature similar to "method categories" in smalltalk. See Creating humane interfaces using AspectJ for more details (uses AspectJ-based examples, but the idea is more general).
[Dave Kirby] December 8, 2005 3:45:24.732
Here is another problem with the Java approach. The doc for List says: "The List interface provides four methods for positional (indexed) access to list elements. Lists (like Java arrays) are zero based. Note that these operations may execute in time proportional to the index value for some implementations (the LinkedList class, for example)." So if your method uses list.get(list.size()-1) and it is passed a linked list with a billion elements in, then you are in big trouble. IMHO this breaks the liskov substitution principle since the code is O(1) for an array-type list and O(N) for a linked list. Of course the LinkedList class has getFirst and getLast, but you cannot use them in your code if you want it to work with any type of list.
Ruby and Java are fundamentally different
[John Lindsey] December 8, 2005 5:10:54.234
As a small aside to the more important debate (intention vs implementation, humane vs minimalist, good OO vs Demeter violations) please, please stop talking about the method count as any sort of meaningful metric: it is an apples to oranges comparison.
In Ruby, an Array is something you can new up and actually use. It mixes in (think inherits from an abstract class, java people) 23 methods from the Enumerable module. It also adds another 11 sugary/c++/operator sort of methods like + & etc.
In java, List is a java construct called an interface. To actually use a list, a typical java programmer will new up a LinkedList (or an ArrayList). A LinkedList implements 6 interfaces, 4 of which are generified in java 1.5. It has 5 parent classes in its parent hierarchy. The resulting surface area is 58 methods (not including the "virtual" surface area of the generics). That does not count static methods in utility classes like Collections that can operate on Lists and adds things like sort, shuffle, min, max, reverse, swap...just the sort of things that the Ruby Array gets from its Mixin.
So the real comparison is 23 + 11 + 44 to 58 + about 30 or so...wow, imagine that; if you think of the common things you would do with a list, Ruby and Java do about the same stuff. They just do it in a different OO structure.
It is also obvious but worth noting that we are looking at the sort of classes that should end up with the largest surface area: basic CS things like Bags, Dictionaries, and Lists are going to have more methods than your average complex business class in your custom app that only your team uses.
Java's Humane Interface!
[Isaac Gouy] December 8, 2005 11:46:56.908
John Lindsey it is an apples to oranges comparison
Indeed, see the comment "It's just an utterly false comparison"
It's wonderful that anyone can associate Minimal with Java, and keep a straight-face :-)
Why not two interfaces?
[Anthony Lauder] December 8, 2005 12:08:56.245
One thing confuses my humble brain about all these posts. Some folks are saying "We can make a minimal interface" and others are saying "a richer interface is more friendly". It seems to me that you can make both groups happy by having both sets of interface: start out with the minimal one at the core, and building an optional "layer" on top of it that adds all the "humane" stuff.
Ruby kind of does that...
[Joe Van Dyk] December 8, 2005 15:32:33.210
Anthony, as another user mentioned, Ruby's Array class "mixes in" the Enumerator module. It's Ruby's way of doing multiple inheritance, I believe.
The Enumerator module gives the Array class these methods:
Here are the Array instance methods, if anyone's interested:
Humane interface debat
[Bernard Notarianni] December 8, 2005 15:52:47.739
Trackback from Bernard Notarianni Humane interface debat A big debat is agitating the blogoshere since Martin Fowler wrote about the “humane” interface style, versus the “minimal” style. An argument from the “minimal” camp has been quoted by James Robertson: A 78 method List class is about three times as ......
What a bunch of nerds!
[The Anti-Nerd] December 8, 2005 19:55:47.320
Java development does NOT always rot the brain, some people write code for a living, not having much time for geek-wannabee discussions as this one. Don't you guys have any work to do?
Intention revealing? Tell me about Ruby's Queue and Stack interfaces.
[] December 9, 2005 12:56:38.788
It always grates on me when, after looking over 10 lines of code involving a List named "arr" that what the guy meant was Stack. If you are really dealing with a Platonic "List", I don't know why you'd need an indexed getter, much less first and last.