• Post Reply Bookmark Topic Watch Topic
  • New Topic
programming forums Java Mobile Certification Databases Caching Books Engineering Micro Controllers OS Languages Paradigms IDEs Build Tools Frameworks Application Servers Open Source This Site Careers Other Pie Elite all forums
this forum made possible by our volunteer staff, including ...
Marshals:
  • Campbell Ritchie
  • Jeanne Boyarsky
  • Ron McLeod
  • Paul Clapham
  • Liutauras Vilda
Sheriffs:
  • paul wheaton
  • Rob Spoor
  • Devaka Cooray
Saloon Keepers:
  • Stephan van Hulst
  • Tim Holloway
  • Carey Brown
  • Frits Walraven
  • Tim Moores
Bartenders:
  • Mikalai Zaikin

Humane Interfaces?

 
Ranch Hand
Posts: 1780
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
I suppose this is forum is the best fit for the following article, and its wake:

http://martinfowler.com/bliki/HumaneInterface.html

Any comments, vis-a-vis Java? I'm of two minds. I'd rather implement
a minimal interface but program to a more humane, or maximal, one.
 
author
Posts: 14112
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Well, as the article is about class design, I'll move it to our OO forum...
 
author and iconoclast
Posts: 24207
46
Mac OS X Eclipse IDE Chrome
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Just some rambling comments...

The whole debate makes me think about TDD (as in "Test-Directed Design") and also the Law of Demeter. Fowler mentions Java's List interface, so let's go with that.

If you're designing a List interface in isolation, you're very likely to come up with something like java.util.List. But if you're using TDD to develop the implementation and interface together, then I think the "Humane Interface" is more likely; i.e.,



Now, I've written a very simple test, and I've got to add two things to ListImpl that current JDK lists don't have: a constructor that takes an Object array (why, oh why, didn't they include these?) and the last() method that Fowler talks about in his article. So by doing TDD, I think you tend to lean toward the "Humane Interface."

The Law of Demeter suggests that it's bad to get too involved with other objects, and offers specific guidelines on avoiding entanglement. Very often, I find that Demeter violations are solved by pushing behavior into an object. Taken literally, Demeter says that iterating over a collection and doing something to each item is wrong. This is not something easy to do in Java, but in Ruby it's solved, more or less, by letting arrays and other collections have methods that accept a code block and execute it for each item in a collection. So following Demeter also leads to enlarging the interfaces of library-like classes.
 
(instanceof Sidekick)
Posts: 8791
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

This is not something easy to do in Java, but in Ruby it's solved, more or less, by letting arrays and other collections have methods that accept a code block and execute it for each item in a collection.



Visitor essentially does this with some extra code in the items in the collection. Visitor was high on the "which pattern would you vote off the island" list at some convention or other. I tend to like it more than most people do, I guess. I think it saved my bacon in a third party collection that was "closed for modification" and in a dynamic tree I had to build at work where there never was a real collection.
 
Ilja Preuss
author
Posts: 14112
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
I don't see how Visitor helps here. I understood Ernest to mean more something along the lines of code blocks - kind of "anonymous strategies". Like the forAllDo method in http://jakarta.apache.org/commons/collections/api-release/org/apache/commons/collections/CollectionUtils.html - just implemented directly on the collection instead of as a static utility method. (That's how Smalltalk does it.)
 
Sheriff
Posts: 7001
6
Eclipse IDE Python C++ Debian Java Linux
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
My take on all this is that there is room for both approaches, even in a single language, even in a single project. I have done it in my designs several times.

As pointed out above. Designing an abstraction from scratch (and typically in isolation) often leads to a kind of "minimalist but complete" solution which might be clumsy for real world use but contains very little duplication.

Similarly, Designing from real requirements (be it TDD or any other similar approach) often leads to a friendly but internally messy and duplicated solution.

My gut feeling is usually to separate the two, but keep them both available where they are used by other code. I've heard this referred to as "refactoring", and I am fairly sure Mr Fowler has also heard the term

Sometimes the "humane" version of a class/interface directly inherits from the "minimal" version, sometimes it delegates, sometimes it's just a collection of static utility methods somewhere (java.util.Collections vs java.util.Collection, anyone?)

The whole "humane interfaces" furore seems like a storm in a teacup to me. Good practice is still the same in both cases :- build what the user needs; refactor to remove duplication; repeat.
 
Stan James
(instanceof Sidekick)
Posts: 8791
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
forAllDo() is interesting. I hadn't looked at that framework. Many of the closures and transformers look like somebody set out to see "How many of these can I make up?" rather than harvesting them from places where they really paid off. I've done that before for sure, like duplicating all my favorite REXX concepts in a few other languages.

Taken literally, Demeter says that iterating over a collection and doing something to each item is wrong.



Does forAllDo() just push this (maybe) "wrong" thing into a library where you can ignore it? Maybe that's ok ... to some degree libraries let you pretend the language supports it. No matter how much we love our OO principles, it's all 1s and 0s down there somewhere.
 
Ilja Preuss
author
Posts: 14112
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Originally posted by Stan James:
Does forAllDo() just push this (maybe) "wrong" thing into a library where you can ignore it?



The Jakarta Commons thing, yes.

The "original" Smalltalk thing, no - there it is implemented directly on the collection, so it's the collection iterating over its own items, which is fine with Demeter.

In Java, you can't add methods to an existing class without touching it's source code, and Sun didn't think about providing that method, so a utility method is the closest you can come. (To be fair, Tigers enhanced for loop comes close.)
 
Ilja Preuss
author
Posts: 14112
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Originally posted by Frank Carver:
As pointed out above. Designing an abstraction from scratch (and typically in isolation) often leads to a kind of "minimalist but complete" solution which might be clumsy for real world use but contains very little duplication.

Similarly, Designing from real requirements (be it TDD or any other similar approach) often leads to a friendly but internally messy and duplicated solution.



I don't understand what a "humane interface" has to do with messy and duplicated solutions. Could you please elaborate? Thanks!
 
Frank Carver
Sheriff
Posts: 7001
6
Eclipse IDE Python C++ Debian Java Linux
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
My use of "messy" was only intended to indicate some sort of design steps or process, rather than an end result. The two sequences I mentioned were roughly:

approach 1 ("from scratch"):
  • idea, theory or model
  • minimal solution
  • add (potentially in more than one place) code which uses minimal solution
  • usable application(s)



  • approach 2 ("from experience"):
  • grab lots of bits used in similar applications
  • merge them together
  • add some "glue"
  • usable application(s)



  • The general problem with approach 1 is the potential duplication at step 3. This "fault line" between system/library software and "application" software has led to a lot of code duplication over the years, and seems to be one of the problems that a "humane" approach aims to solve.

    The general problem with approach 2 is that code is put into the codebase before it is known if it is needed or not. Depending on the origin and state of this code (some my be copy-n-paste, some may be re-implementation of a familiar pattern) this may involve duplication, or may need adapting to fit the new context. Any such "messiness" can be fixed, but the end result will still be more code than may actually be needed.

    My preferred solution is to include some of both these approaches.

    Start by building just the code that is needed at any point in time. At the start of a project that may just be a small number of "humane" methods or a concise and minimalist model. As more functionality is added to the application, there becomes more pressure for more methods.

    This is the point at which the minimalist/humane decision becomes important - my response is to follow the need for simplicity by deriving a concise, consistent and individually testable "core", but also to follow the need for suiting the calling code and removing duplication by adding an extra layer of more "friendly" but potentially less generic interfaces.

    Or am I still missing the point?
     
    reply
      Bookmark Topic Watch Topic
    • New Topic