Home

News: Move over Minimalist, here comes Humane

  1. Move over Minimalist, here comes Humane (15 messages)

    In an interesting two-part blog series, Martin Fowler does a point/counterpoint on himself on the subject of Humane vs. Minimalist interface design. Though Martin attempts to make a reasonable case for Minimalist interfaces, it takes a rebuttal blog from Elliotte Rusty Harold to come to the rescue of that argument.

    Martin's statement that defines the debate clearly shows his bias towards the Humane argument.
    Minimalists tend to focus on the minimal set of necessary methods to support these behaviors, humane designers try to add methods that are needed. Often these extra methods are referred to as convenience methods, a term that minimalists do not consider to be a complement.
    This statement is followed up by two examples taken from a comparison of the first, last and flatten methods found in Ruby's Array class that are not found in Java's List Interface. In the counter arguement against, Rusty uses some words that should be familiar to Martin.
    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 minimalist argument is that List (or Array) doesn’t *need* a first or last because that functionality is already contained in the get(int index) method. Further more the existence of a method to flatten an array of arrays would seem quite frivolous to a minimalist. After all, when is the last time that you honestly remember needing to flatten and array of arrays (or list or lists). This example clearly shows the distance between the two positions.

    This distinction in wasn’t lost on the blogging community as many people jumped on the bandwagon. Not surprisingly many of those blogs were centralist as they failed to fully support either position. However the futility of the debate is questioned in a very thoughtful blog written by John D. Mitchell.
    Alas, arguing back and forth over those sorts of details makes it easy to miss a fundamental, crucial point: no software (library, application, language, operating system, or whatever) can be all things to all people. Fighting that war is not only pointless but is one of my definitions of insanity. The point of a chunk of good software is to enable the effective and efficient creation of more good software and to help inhibit the creation of bad software.
    Does all of this mean that one of our leading thought leaders turned a complete 180 on his previous musing on interface design?
  2. How many too much?[ Go to top ]

    Well, the point boils down to the question how many methods should object have, minimum to keep interface simple or as many as context demands.

    I feel interface should be simple but having many methods is not a sin, depending on situation. We need to use our sense of descretion, and in few cases if we have more than enough, making interface not-so-simple, it is okay.

    There is also middle way: We can keep the object interface simple by providing minimum methods and have the object hold an instance of extension/helper object, which can have those extra methods.
  3. The minimalist counter argument is making a number of errors:

    - Ruby isn't Java: the language promotes the use of lists and list processing - a flatten method is as useful in ruby as it is in Lisp

    - Ruby has mix-ins: when you look at objects that have large numbers of methods you're often looking at an amalgam of a class and its modules. The counter argument says in Java that the Collections class is a properly designed object that offloads those kinds of methods. In ruby a mix-in would serve the same purpose but you'd have the methods exposed on the object - where they're easy to find and easy to use.

    - The Array class they've chosen to argue about is a fundamental datatype in Ruby: Java often makes the mistake of trading theoretical goodness for real-world usefulness. How many times have you worked on someone else's code and found multiple utility classes for operations on Strings, Arrays, Lists, etc.? They all implement many of the same methods but some write them correctly and some don't. Are we doing the Java community a service or a disservice by not moving those common methods into the basic datatype classes?

    The minimalist approach is an excellent rule-of-thumb. It's the standard by which you should design but it's not a straight-jacket. When you make the statement that a 12 method class is twice as good as a 24 method class then you're clearly going overboad. A design should meet the needs of its users while trying to be clean, concise and testable. That's the standard by which you design, not arbitrary rules.
  4. As its always in Software design, design of some sort depends on numerous things including its usage.

    It all depends on the language, library and people who use them (I guess Ruby developers are lazy and don’t mind polluted interfaces).

    I would say just start with close to minimalist approach and refactore it forward as its usage grows.
  5. I have blogged about Creating humane interfaces using AspectJ.

    With AspectJ's inter-type declaration feature, you create core interfaces following the minimalist approach and use aspects to introduce convenience methods, along with their implementation, to those interfaces. This style also offers a way to customize the interfaces to suit individual project's need. After all, one project's convenience may be other's nuisance.

    -Ramnivas
  6. This style also offers a way to customize the interfaces to suit individual project's need. After all, one project's convenience may be other's nuisance.-Ramnivas

    Very good point indeed. One might say that aspects are overkill solution to this problem but on the other hand multiple helper classes (StringUtils, ArrayUtils, etc.) is not very elegant solution either.

    If aspect technology is used in a project anyway, I would use this approach. However, if aspect technology is not already in use, I am not sure if it is appropriate to introduce aspects just to solve this problem...
  7. One might say that aspects are overkill solution to this problem but on the other hand multiple helper classes (StringUtils, ArrayUtils, etc.) is not very elegant solution either.If aspect technology is used in a project anyway, I would use this approach. However, if aspect technology is not already in use, I am not sure if it is appropriate to introduce aspects just to solve this problem...
    This only means that aspects are not as commonplace as plain OO is. Not that aspects are overkill. If aspects were adopted then OO might be overkill :-)
  8. This is a programming style that I think could do with more exposure. Treating objects as having multiple facets which allow the object to be viewed differently in different situations.

    The nearest solution in Java 1.4 I think is to have a:

    Object asFacet(Class clazz)

    method on your facet'ed objects.

    Then you can have

    MyPureInterface pure = someFactory.getPureObject();

    where pure has say just one method:

    doCleanMethod();

    and then:

    LessPureInterface pure.asFacet(LessPureInterface.class);

    where LessPureInteface has many methods for nitty gritty work.

    The asFacet would then instantiate an instance of LessPureInterface and then inject the pure object. Of course in Java 5.0 I imagine the syntax would be a lot, lot cleaner.

    More importantly I think it's the design concept that an object appears different to different consumers while avoiding inheritance as the basis and limit of polymorphism (this includes allowing an object to become other objects beyond those specified by the original author/designer).

    And yes I agree, however it's implemented has to be better than helper classes eh!
  9. It is always better to start with a Minimalist approach.
    Considering Martin Fowlers comments on the List class of java and Ruby, it is always better to leave some operations to be performed on the client code rather than doing it in some of the core classes. For example when working with arrays some functionalities should be left to the client so that the best possible algorithm for a particular scenario could be used.
  10. The minimalist argument is that List (or Array) doesn’t *need* a first or last because that functionality is already contained in the get(int index) method.

    Ahem. So instead of writing X.last(), I should write X.get(X.size())? Or was it size - 1? And that oppertunity for error is present each time.

    Based on the rather universal accepted DRY (Don't Repeat Yourself) moving as much code over to a central class does have a sound motivation. It does make the central class more complex to test, but the code has to go somewhere! And if it isn't in the central class, then it is somewhere else and has to be tested and debugged anyhow. Probably even multiple times, because we repeated ourselves (as with the last example).

    Everything is a balance.
  11. What about just applying some technical criteria in design, like aiming for higher cohesion and lower coupling?
    Anyway, all this "humane ve minimalist" polarization brings me memories from the RISK vs CISC wars...
  12. For the same token, one could argue List and Array does not belong to the core Java library because it makes Java hard to learn as a new user will always learn the java.* API to do any programming in Java.

    One of the great strengh of Java over C/C++ is the standardization of core packages and classes (and thus methods)

    Similar to the Conservation of Mass-Engery in physics, I call this the Conservation Law of Coding-Learning. We have to either write the same codes or learn another similar API. Approaches like AOP just shift the difficulty to another type of interface.

    I don't find the Liat/Array/Collections classes too hard to learn or over complex because most mehods there are quite common and useful. And still we have to write additional utility classes for some operation we need.

    The rule of thumb is simple: if a method can be used by a reasonable percentage of programmers, then it should and will go into the API, either in the core class or in a utility class.
  13. Elliotte is right, having 78 methods in a class is an atrocity, except in some special cases (java.lang.String has about 60, which does not hurt at all).

    But I think, Elliotte misses a crucial point: While some of Java's core classes indeed do follow a minimalistic philosophy compared to other languages, there a many others which are overly complex. How many lines of code do we need to send out a trivial e-mail with a file attachment? How many lines are necessary for setting up a simple SAX processing pipeline or a DOM document builder? Or to play an audio clip? These are indeed relevant questions for all-day developers, and they have been neglected by the Java academics.
  14. JDK Minimalist?[ Go to top ]

    The thing I don't get about this discussion is that I don't think that the List interface is minimalist. Here are a minimum set of methods from the List interface that are not necessary:

    addAll(Collection c)
    addAll(int index, Collection c)
    clear()
    containsAll(Collection c)
    indexOf(Object o)
    isEmpty()
    lastIndexOf(Object o)
    remove(Object o)
    removeAll(Collection c)
    retainAll(Collection c)
    set(int index, Object element)
    subList(int fromIndex, int toIndex)
    toArray()
    toArray(Object[] a)

    I tend to favor the minimalist argument when it comes to interfaces. The reson being that having all these methods on the interface makes it much harder to implement the interface.

    The map inteface is a better example. Arguably, the map interface should have two methods: keySet() and get(key) and a ModifiableMap should add put(key, value). All the other stuff is great for people using the class, but for implementors, it's a huge pain the butt. If I want to provide a method on my interface that returns a map representation of my Object, I have to implement a lot of methods that I probably won't want to have.

    I'm not saying that the JDK shouldn't have a class that provides all kinds of goodies for working with collections, it's just that this class should not be a base interface type.
  15. having all these methods on the interface makes it much harder to implement the interface.
    ...
    Arguably, the map interface should have two methods: keySet() and get(key) and a ModifiableMap should add put(key, value).

    By extending from an abstract base class, implementers only work with the core minimalist interface. Immutable AbstractMap subclasses only need implement entrySet(); mutable subclasses must additionaly implement put(key, value) and ensure the entry set's iterator implements remove().
  16. having all these methods on the interface makes it much harder to implement the interface....Arguably, the map interface should have two methods: keySet() and get(key) and a ModifiableMap should add put(key, value).
    By extending from an abstract base class, implementers only work with the core minimalist interface. Immutable AbstractMap subclasses only need implement entrySet(); mutable subclasses must additionaly implement put(key, value) and ensure the entry set's iterator implements remove().

    Yes I am aware of this. Implementing entrySet means implementing or providing a set and Map.Entry. I think entrySet is overkill for the Map interface. In addition this implementation is extremely naive having O(n) performance on get(). And obviously, if your class need to extend something other than AbstractMap, this doesn't help much.

    I was originally going to say that a truly minimalist unmodifiable Map would only have get(). I think that's really the point here. If I want to 'recast' an Object as a Map, I'm often not going to need all this other baggage.