The Artima Developer Community
Sponsored Link

Weblogs Forum
Readable Method Calls in Java

30 replies on 3 pages. Most recent reply: Aug 5, 2005 1:52 AM by Matt Gerrans

Welcome Guest
  Sign In

Go back to the topic listing  Back to Topic List Click to reply to this topic  Reply to this Topic Click to search messages in this forum  Search Forum Click for a threaded view of the topic  Threaded View   
Previous Topic   Next Topic
Flat View: This topic has 30 replies on 3 pages [ « | 1 2 3 | » ]
Vesa Karvonen

Posts: 116
Nickname: vkarvone
Registered: Jun, 2004

Re: Readable Method Calls in Java Posted: Jul 13, 2005 1:29 PM
Reply to this message Reply
Advertisement
I generally use enumerated constants in cases like this instead of a boolean.

Kevin Lawrence

Posts: 13
Nickname: kevlaw
Registered: Feb, 2005

Re: Readable Method Calls in Java Posted: Jul 13, 2005 2:53 PM
Reply to this message Reply
First of all, I agree with Michael's solution but ....

.... don't you think the problem is a little overstated ?

Isn't this ....

"Of course, you can search for the addValue method, navigate to it, and read it to find out what the values mean, but that means you're no longer reading the code, so much as translating it. It's as though you're reading a book written in a foreign language, with the book in one hand and a translation dictionary in the other. If you only have to do it once in a while, it's not too bad. But if you're doing it all the time, it becomes a problem."

... a rather long winded way of saying ...

"mouse over the method and read the tooltip"


????

Todd Blanchard

Posts: 316
Nickname: tblanchard
Registered: May, 2003

Not a bad workaround Posted: Jul 13, 2005 4:50 PM
Reply to this message Reply
But your post mostly points up the poor choice of syntax chosen for Java in the first place. This is unfortunate considering the long standing better syntax of Smalltalk for OO languages.

We could have

object addValue: 13 atLocation: 47 overwrite: true.

Which you can read out loud and sounds like a sentence.

When I work in Java or C++, I still adopt this naming convention.

object.addValueAtLocationAndOverwrite(13,47,true);

Bruce Eckel

Posts: 874
Nickname: beckel
Registered: Jun, 2003

Built-int Feature in Python Posted: Jul 13, 2005 5:08 PM
Reply to this message Reply
So I'm curious -- is this something you saw in Python and thought "That's nice, can we do something like it in Java?" Personally, I never thought of doing it before seeing it in Python. Simple example:


def f(foo, bar) : pass

f(foo = 1, bar = 2)


Function or method calls can always use parameter names, but this has the additional benefit of working with default arguments. That is, you can leave all the defaults on, and use a named argument to change one of the defaults. It's quite nice -- unfortunately they were deemed "bad" by the Java designers.

Keith Ray

Posts: 658
Nickname: keithray
Registered: May, 2003

Re: Readable Method Calls in Java Posted: Jul 13, 2005 11:14 PM
Reply to this message Reply
> This is true if we control the code and can change the
> method name but the problem still remains if we are using
> a badly named library method whose signature we cannot
> change.

Our industry needs to start demanding better names. If the library is small, than writing a well-named set of adapters could be worth the investment in reducing bugs. (And could allow switching to a better library later.)

In regards to the "overhead" of classes like "Value" and "Position" - in some languages, those are just typedefs (Java is a bit deficient in this regard). This series of postings started with a contrived, simple example. In the real world, there could be a whole class's worth of behavior that are just waiting to be refactored into "Value" and "Position".

Michael Feathers

Posts: 448
Nickname: mfeathers
Registered: Jul, 2003

Re: Readable Method Calls in Java Posted: Jul 13, 2005 11:57 PM
Reply to this message Reply
> First of all, I agree with Michael's solution but ....
>
> .... don't you think the problem is a little overstated ?
>
> Isn't this ....
>
> "Of course, you can search for the addValue method,
> navigate to it, and read it to find out what the values
> mean, but that means you're no longer reading the code, so
> much as translating it. It's as though you're reading a
> book written in a foreign language, with the book in one
> hand and a translation dictionary in the other. If you
> only have to do it once in a while, it's not too bad. But
> if you're doing it all the time, it becomes a problem."
>
> ... a rather long winded way of saying ...
>
> "mouse over the method and read the tooltip"

Yeah, but I think there is a problem either way. When I look at a piece of code I want to be able to grok it immediately. I don't want to roam across the screen hunting for buried treasure, using my mouse as the metal detector.

Dirk Detering

Posts: 16
Nickname: det
Registered: Jul, 2005

Re: Readable Method Calls in Java Posted: Jul 14, 2005 3:50 AM
Reply to this message Reply
>> ... a rather long winded way of saying ...
>>
>> "mouse over the method and read the tooltip"
>
> Yeah, but I think there is a problem either way.
> When I look at a piece of code I want to be able to
> grok it immediately.
> I don't want to roam across the screen hunting for
> buried treasure, using my mouse as the metal detector.

And this is even more true when you think of code as a 'content' and not a 'document format'. PDF/HTML/... content should be all readable even if you don't have Acrobat-Reader/Browser/..., e.g. when copy/pasted into an eMail, posted in a forum, perhaps simply opened in a 500kB Text-Editor (which does not analyse a complete Java installation to get the information I could perhaps need to understand the content), or even printed on paper.

I like the support of modern IDE's especially for quickly writing code, navigating, managing projects a.s.o., but I don't want to be bound to them as necessary Code-Reader application.

(BTW: You never saw a tooltip like: "setBounds(int, int)" ?)

Terje Slettebø

Posts: 205
Nickname: tslettebo
Registered: Jun, 2004

Re: Readable Method Calls in Java Posted: Jul 14, 2005 7:02 AM
Reply to this message Reply
>int value; int location; boolean overwrite;
> ...
>
>doIt(value=13, location=47, overwrite=true);
>
>With this mechanism, the method call doIt(value=13, >location=47, overwrite=true) now accomplishs three goals, >all at the same time, in the least possible space:
>
>- It makes it clear that you're passing two integers and a >boolean.
>- It specifies the values you're passing.
>- It names the values, so you know what purpose they serve.
>
>Finally, note that the ordering of these "named
>parameters" is always preserved. The names exist for
>semantic clarification--they do not permit you to change
>the order of the parameters--a facility which tends to
>produce readability problems of its own.

Whether being able to change the order of the arguments is an advantage or disadvantage is debatable, but I note that this approach has at least a couple of shortcomings compared to "real" named argument support:

1) One problem with this approach is that it doesn't detect arguments in the wrong order (the variable names used are not understood by the compiler - they are little more than comments). The compiler will equally well accept:

doIt(location=47, value=13, overwrite=true);

(which calls doIt() with value=13 and location=47...)

Thus, the above may give a false sense of security.

2) It doesn't let you to skip default arguments "in the middle" - one of the major reasons for having named arguments in the first place. I.e. being able to provide argument 1 to 3, and 6, and have 4 and 5 defaulted.

Regards,

Terje

Howard Lovatt

Posts: 321
Nickname: hlovatt
Registered: Mar, 2003

Re: Readable Method Calls in Java Posted: Jul 15, 2005 2:22 AM
Reply to this message Reply
I don't think this is a big problem. It can be tackled with a combination of the following:

1. Use a descriptive name, instead of:
o.add( 12, 47, true )

Try:
o.addValueAtLocationWithOverwite( 12, 47, true )


2. Use an enum instead of boolean, e.g.:
o.addValueAtLocation( 12, 47, Overwrite.YES )


3. Use classes instead of primitives (in general - not just boolean), e.g.:
o.add( new Value( 12 ), new Location( 47 ), Overwrite.YES )


4. add comments, e.g.:
o.add( /*value*/12, /*location*/47,  /*Overwrite*/true )


5. split the call, e.g.:
o = new OType( o ).overwrite( true ).location( 47 ).value( 12 )


In summary, there are so many call options already in Java it doesn't seem worth adding another to me. However if another call option were to be added then I would favour mimicing the annotations syntax, e.g.:
o.add( value=12, location=47, overwrite=true )

and allowing default values, e.g.:
public void o.add( final int value default 0, final int location default 0, final boolean overwrite default false ) {
    ...
}

Dirk Detering

Posts: 16
Nickname: det
Registered: Jul, 2005

Re: Built-int Feature in Python Posted: Jul 15, 2005 3:17 AM
Reply to this message Reply
@Bruce Eckel:

> I never thought of doing it before seeing it in Python.

Same for me.

A similiar functionality is simulated in Ruby, and I think in Perl too, by implicitly creating a Map as Parameter when detecting a reduced Map syntax in the argument.
(quote from 'Programming Ruby':
"So, Ruby has a short cut. You can place key => value pairs in an argument list, as long as they follow any normal arguments and precede any array and block arguments. All these pairs will be collected into a single hash and passed as one argument to the method. No braces are needed. ")
It is more handcraft than in Python indeed to provide that feature.

Beside that: At this time this topic is under discussion on the Groovy user list.

Dirk Detering

Posts: 16
Nickname: det
Registered: Jul, 2005

Re: Readable Method Calls in Java Posted: Jul 15, 2005 4:14 AM
Reply to this message Reply
> o.addValueAtLocationWithOverwite( 12, 47, true )

Folks, can't we get rid of this (unfortunately in Java often seen) methodnaming construct?

It is a bit odd IMHO to add a value with overwrite. You simply overwrite:
o.overwriteValueAtLocation

(well, 'overwrite', if you indeed need a differenciation between 'overwrite' (and add if not existing) and 'update' (and report an error if not existing) and 'insert' (and
report an error if existing) ).

Regarding the examples in Howard Lovatt's post:

> 3. Use classes instead of primitives
> (in general - not just boolean), e.g.:
>
> o.add( new Value( 12 ), new Location( 47 ),
> Overwrite.YES )

a) Introducing new class types only to semantically
markup the integer values is oversized.
This only makes sense if 'Value' and 'Location'
take more responsibility but to carry an integer value.
b) If this responsibility is given and classes
are introduced, 'Location' is something which is better
maintained by the container 'o'

So this leads to either getting the Location with o.getLocation(47) as I mentioned before.

OTOH: Why should the user of 'o' now such implementation details and talk to a 'Location' object ? (Law of Demeter)
So you're back to an API on 'o' with simply putting in values:
o.overwriteValueAtLocation(12,47)



> 5. split the call, e.g.:
> o = new OType( o ).overwrite( true ).location( 47 ).value( 12 )

Leads much too far from the way into difficult terrain to my mind.

No solution for the primary targeted problem. At least I don't want to need writing such code ...

My resume until now:

Eric's article put his finger on a programmer's daily problem. This article and the responses here (in the end the post of Howard) show different possibilities to solve the problem.

It is now a programmer's responsibility to take the knowledge summed in the responses to decide in his daily work, which approach is the best for him.
Especially as Class/Method developers we should think twice (or more) about the API we deliver and if we could make things easier for the user by renaming/redesign.

Howard Lovatt

Posts: 321
Nickname: hlovatt
Registered: Mar, 2003

Re: Readable Method Calls in Java Posted: Jul 17, 2005 9:21 PM
Reply to this message Reply
I can add another two methods of tackling this problem to my list of 5 (see above):

6. Collect all the arguments into a class:
o.add( new AddArgs().value( 47 ).location( 12 ).overwrite( true ) )


7. Use an enum to define the argument name and default value, use a varargs list to specify the arguments, and convert the varargs to a map. First an infrastructure class that would be shared accross all the methods:
class Args {
    public interface DefaultValue { Object getDefault(); }
 
    public static class ArgsMap< K extends Enum< K > > extends EnumMap< K, Object > {
        public ArgsMap( final Class< K > clazz, final Object... args ) {
            super( clazz );
            try {
                for ( K key : (K[]) clazz.getMethod( "values", (Class[]) null ).invoke( null, (Object[]) null ) )
                    put( key, ( (DefaultValue) key ).getDefault() );
            } catch ( /* I cant rememeber the exceptions of the top of my head! */ ) { throw new RuntimeException( e ); }
            for ( int i = 0; i < args.length; i += 2 )
                put( (K) args[ i ], args[ i + 1 ] );
        }
    }
 
    public static < K extends Enum< K > > ArgsMap< K > argsMap( final Object... args ) { return new ArgsMap< K >( args ); }
}

You can specify the argument names and default values with an enum:
enum AddArgs implements DefaultValue {
    VALUE( 0 ), LOCATION( 0 ), OVERWRITE( false );
    private final Object value;
    AddArgs( final Object value ) { this.value = value; }
    public Object getDefault() { return value; }
}

You can define the method to take varargs and map these:
import static Args.*;
...
    public void add( final Object... args_ ) {
        final ArgsMap< AddArgs > args = argsMap( AddArgs.class, args_ );
        ...
    }

The usage has very good syntax:
import static AddArgs.*;
...
     o.add( VALUE, 12, OVERWRITE, true );

The main downside is that you loose some compile time checks. It is also a little slow. However both these issues are true of other languages that support this type of argument passing, e.g. Ruby and Python.

Ben St. John

Posts: 3
Nickname: jbstjohn
Registered: Aug, 2005

Re: Readable Method Calls in Java Posted: Aug 2, 2005 11:40 AM
Reply to this message Reply
Interesting topic.

I generally am against the whole create-an-object approach, although I have to admit, I come more from the C++ (and embedded side) than Java.

I also think it's an important constraint that the function name is restricted (more or less).

Given all this, I think there are two different problems that need to be clarified. The first is the boolean argument, and I agree with other posters that have suggested renaming the function, and removing the boolean argument.

Regarding the previously mentioned constraint, you don't have to rename the function -- write two short helper functions that then call the original one with the correct argument.

This does get a bit ugly if the number of booleans increases, but then it's often worth collecting them in a meaningful structure/type/object.

The other readability problem are the numbers. Yes, the name of the function should say more. But what are magic numbers like this doing in the code anyway? I know it's a contrived example, but 13 & 47? To me, if you solve this problem, you solve the readability problem.

I actually find it difficult to think of some constant, that's a location, and is an integer. Very likely, it's a index into a table, which should also then be in the name, but here goes:

Something like (pick your form for writing constants):
incrementValue(KHeaderLength, KFileLengthID);

or:

incrementProperty(KHeaderLength, EFileLength);

Of course, this illustrates that without actually knowing what the function should do, it's hard to pack more information into calling it!

I also find the commenting way not too bad. For particularly nasty calls, I've sometimes copied (commented out) the function prototype, but this has maintenance issues.

Cheers,
Ben

Jeremy Huiskamp

Posts: 3
Nickname: kamper
Registered: Feb, 2005

Re: Readable Method Calls in Java Posted: Aug 2, 2005 4:26 PM
Reply to this message Reply
My personal opinion is that no solution is really good enough in java as it is right now. I like the idea of the language-level fix: using named maps for parameters instead of ordered lists. I know only a smidgen of python and no smalltalk but that's sort of what I see from the examples you guys have posted.

So for the method
void doStuff(int foo, int bar, boolean trueOrFalse)
the following calls would be legal and equivalent:
doStuff(foo = 1, bar = 2, trueOrFalse = true);
doStuff(foo = 1, trueOrFalse = true, bar = 2);
doStuff(trueOrFalse = true, bar = 2, foo = 1);
...

But in the mean time, I agree with Ben here. If you've got bare numbers like 13 and 47 in the middle of your code there are bigger problems. Make them constants with descriptive names which tell you not only what the type means, but also the specific value. If doing that gets tedious it's a sign you have too many magic numbers and you should be looking at another solution anyways.

Matt Gerrans

Posts: 1153
Nickname: matt
Registered: Feb, 2002

Re: Readable Method Calls in Java Posted: Aug 5, 2005 1:46 AM
Reply to this message Reply
I only hope you are joking here.

This is kind of like using a double-barrelled shotgun to shoot a mosquito off your forehead.

Flat View: This topic has 30 replies on 3 pages [ « | 1  2  3 | » ]
Topic: Generics vs. Dynamic Checking Previous Topic   Next Topic Topic: Are Tests First Class Clients?


Sponsored Links



Google
  Web Artima.com   

Copyright © 1996-2014 Artima, Inc. All Rights Reserved. - Privacy Policy - Terms of Use - Advertise with Us