Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > Java > refactoring

Reply
Thread Tools

refactoring

 
 
Roedy Green
Guest
Posts: n/a
 
      08-06-2007
I often run into this situation and wonder if there are clever ways of
handling it I have not thought of.

I have a hunk of messy code I would like to break off from a method
and make into its own small method. I use Intellij refactoring
"Extract Method" but it tells me I can't do that because the code
fragment has multiple outputs, e.g. computing two or three static
final local booleans.

I have always felt that a if a method can have multiple inputs it
should be able to have multiple outputs, but very few language
designers (Forth and PostScript being exceptions) have agreed.

It seems such a production to wrap three booleans as if for transport
to Africa in their own class, in an array, (not to mention the
unpacking code) when at the machine level what I am doing is trivial
in assembler..
--
Roedy Green Canadian Mind Products
The Java Glossary
http://mindprod.com
 
Reply With Quote
 
 
 
 
Eric Sosman
Guest
Posts: n/a
 
      08-06-2007
Roedy Green wrote On 08/06/07 16:19,:
> I often run into this situation and wonder if there are clever ways of
> handling it I have not thought of.
>
> I have a hunk of messy code I would like to break off from a method
> and make into its own small method. I use Intellij refactoring
> "Extract Method" but it tells me I can't do that because the code
> fragment has multiple outputs, e.g. computing two or three static
> final local booleans.
>
> I have always felt that a if a method can have multiple inputs it
> should be able to have multiple outputs, but very few language
> designers (Forth and PostScript being exceptions) have agreed.
>
> It seems such a production to wrap three booleans as if for transport
> to Africa in their own class, in an array, (not to mention the
> unpacking code) when at the machine level what I am doing is trivial
> in assembler..


Lots of things are easy in assembly code but difficult
in higher-level languages: unrestricted GOTO, type-punning,
self-modifying code, ... But yes: Some far less iniquitous
things are also hard to do: Getting both the quotient and
the remainder from one integer division, for example, which
is perhaps the situation in which I most miss multiple-valued
expressions.

The only way I know to deal with such issues (without
the bother of inventing a "container" of some kind) is to
study whether the computations of the two or three things
can be untangled from each other. Sometimes they are too
intimately connected to be divorced: like the quotient and
remainder, for example. But quite often they can be pulled
apart so that the messy code divides into multiple methods,
each returning one of the values.

One avenue that is sometimes fruitful -- especially with
flags, for some reason -- is to reconsider the data being
computed. Can the data items themselves be "orthogonalized"
by representing the same information differently? Maybe the
three flags really ought to be one enum, for example.

--
http://www.velocityreviews.com/forums/(E-Mail Removed)
 
Reply With Quote
 
 
 
 
Chris
Guest
Posts: n/a
 
      08-06-2007
Roedy Green wrote:
> I often run into this situation and wonder if there are clever ways of
> handling it I have not thought of.
>
> I have a hunk of messy code I would like to break off from a method
> and make into its own small method. I use Intellij refactoring
> "Extract Method" but it tells me I can't do that because the code
> fragment has multiple outputs, e.g. computing two or three static
> final local booleans.
>
> I have always felt that a if a method can have multiple inputs it
> should be able to have multiple outputs, but very few language
> designers (Forth and PostScript being exceptions) have agreed.
>
> It seems such a production to wrap three booleans as if for transport
> to Africa in their own class, in an array, (not to mention the
> unpacking code) when at the machine level what I am doing is trivial
> in assembler..


I've often wondered the same thing. Fortunately, with modern garbage
collectors creating a wrapper class is not that expensive.

Another method I sometime use is to pack returns in an int or long. For
your boolean example, you can set bits in an int and return it. Ugly,
but fast. Somewhat less ugly, when I need to return two ints I'll pack
them into a long.


 
Reply With Quote
 
Michael Jung
Guest
Posts: n/a
 
      08-06-2007
Roedy Green <(E-Mail Removed)> writes:
> I often run into this situation and wonder if there are clever ways of
> handling it I have not thought of.


We don't knowwhat you have already thought of.

> I have always felt that a if a method can have multiple inputs it
> should be able to have multiple outputs, but very few language
> designers (Forth and PostScript being exceptions) have agreed.


Corba IDL is another and the corresponding compilers generate Java code,
illustrating how this is done in Java alone. Holder classes. Has the
disadvantage of not forcing you to return something.

Otherwise, I agree. Multiple return values without the overhead would be
nice.

How about something along

"public double,double calcModAngle(double re, double im);" ?


Michael
 
Reply With Quote
 
Stefan Ram
Guest
Posts: n/a
 
      08-06-2007
Roedy Green <(E-Mail Removed)> writes:
>should be able to have multiple outputs, but very few language


One idea not mentioned yet is a kind of »continuation passing
style« (»actor style«). The client's method »continuation« is
called by the server to »return« the result. Here the client
gets /two/ random numbers from /one/ call to »getPair« without
an object or an array holding these two numbers.

class Example implements Client
{ public void continuation( final int x, final int y )
{ java.lang.System.out.println( x + ", " + y ); }
public void main()
{ Server.getPair( this ); }}

class Server
{ static java.util.Random rand = new java.util.Random();
static void getPair( final Client client )
{ client.continuation( rand.nextInt( 11 ), rand.nextInt( 21 )); }}

public class Main
{ public static void main( final java.lang.String[] args )
{ new Example().main(); }}

interface Client { void continuation( int x, int y ); }

4, 3

When the program does not need to be threadsafe, the function
and its result can be made static and public to avoid any
overhead by object allocation and getter methods.

public class Main
{ public static void main( final java.lang.String[] args )
{ Calculator.x = 1;
Calculator.y = 2;
Calculator.sumDiff();
java.lang.System.out.println( Calculator.sum );
java.lang.System.out.println( Calculator.difference ); }}

class Calculator
{ public static int x;
public static int y;
public static int sum;
public static int difference;
public static void sumDiff(){ sum = x + y; difference = x - y; }}

 
Reply With Quote
 
Patricia Shanahan
Guest
Posts: n/a
 
      08-06-2007
Roedy Green wrote:
> I often run into this situation and wonder if there are clever ways of
> handling it I have not thought of.
>
> I have a hunk of messy code I would like to break off from a method
> and make into its own small method. I use Intellij refactoring
> "Extract Method" but it tells me I can't do that because the code
> fragment has multiple outputs, e.g. computing two or three static
> final local booleans.
>
> I have always felt that a if a method can have multiple inputs it
> should be able to have multiple outputs, but very few language
> designers (Forth and PostScript being exceptions) have agreed.


Matlab is another exception.

> It seems such a production to wrap three booleans as if for transport
> to Africa in their own class, in an array, (not to mention the
> unpacking code) when at the machine level what I am doing is trivial
> in assembler..


I try not to think in assembler when coding in Java, or think in Java
when coding in Matlab... Most languages have some things that can be
said simply and directly, and other things that take longer.

For the specific case of three booleans, I would question whether there
should be an enumeration naming the conditions, with the method
returning an EnumSet.

However, I'm sure you don't just mean the specific case of booleans.

If the code only runs in one thread, it can sometimes be helpful to
first convert the local variables to fields. That allows the method
extraction. Sometimes, further refactoring comes to light after the
extraction that gets rid of the fields again.

Patricia


 
Reply With Quote
 
andrewmcdonagh
Guest
Posts: n/a
 
      08-06-2007
On Aug 6, 9:19 pm, Roedy Green <(E-Mail Removed)>
wrote:
> I often run into this situation and wonder if there are clever ways of
> handling it I have not thought of.
>
> I have a hunk of messy code I would like to break off from a method
> and make into its own small method. I use Intellij refactoring
> "Extract Method" but it tells me I can't do that because the code
> fragment has multiple outputs, e.g. computing two or three static
> final local booleans.
>
> I have always felt that a if a method can have multiple inputs it
> should be able to have multiple outputs, but very few language
> designers (Forth and PostScript being exceptions) have agreed.
>
> It seems such a production to wrap three booleans as if for transport
> to Africa in their own class, in an array, (not to mention the
> unpacking code) when at the machine level what I am doing is trivial
> in assembler..
> --
> Roedy Green Canadian Mind Products
> The Java Glossaryhttp://mindprod.com


Hi Roedy,

The others have discussed the multiple returns values PoV, so I'll
just mention about the refactoring itself.

When IntelliJ/Eciplse moans about 'multiple outputs', its usually one
of two signs:
1) I'm trying to pull too much into a single method, and so to fix
this, pull 3 (in your example) sets of smaller code into 3 smaller
methods.

2) The ordering of the variables being used within the original
method are preventing the extraction. To fix this, re-arrange the code
within the original method so that the new method does not need to
return 3 outputs, but instead it can then return a different value.

Feel free to post your original method if you'd like me to show you
what I mean using your code.

HTH


Andrew

 
Reply With Quote
 
Michael Jung
Guest
Posts: n/a
 
      08-07-2007
http://www.velocityreviews.com/forums/(E-Mail Removed)-berlin.de (Stefan Ram) writes:
> Roedy Green <(E-Mail Removed)> writes:
> >should be able to have multiple outputs, but very few language

>
> One idea not mentioned yet is a kind of »continuation passing
> style« (»actor style«). The client's method »continuation« is
> called by the server to »return« the result. Here the client
> gets /two/ random numbers from /one/ call to »getPair« without
> an object or an array holding these two numbers.


I think this solution is for a trip to Africa, as Roedy originally put it

> When the program does not need to be threadsafe, the function
> and its result can be made static and public to avoid any
> overhead by object allocation and getter methods.

[...]

One of the things that always strikes me is that explains a lot of inefficient
code. Here's an example held artificially simple.

double a = above(x);
double b = below(x);

double above(double x) { return (int)(2*Math.abs(Math.sin(x)))/2.0; }
double below(double x) { return 1 - above(x); }

vs.

double a,b = aboveandbelow(x);

double aboveandbelow(doubl x) {
double t = (int)(2*Math.abs(Math.sin(x)))/2.0;
return t, 1 - t;
}

It should be obvious that calculating individually is more costly
than as a whole. (*)

But as code evolves, you suddenly find out you need need more results from a
method than before. Sometimes refactoring takes place at the right time, but
sometimes it doesn't and you are either stuck with two methods doing the
almost the same thing or one method returning an "arbitrary" array.

(*) And yes, I am aware that b = 1 - a in the first case. But what if you do
not really know the implementation?) I'm pretty sure that a lot of methods
even in the JDK Java packages show this problem, just because it would have
broken the developper's heart to create an auxiliary class to return two
results., say in- (The whole point construct in Swing could be seen as a point
in case as well. There's a lot of garbage floating around.)

Michael
 
Reply With Quote
 
Roedy Green
Guest
Posts: n/a
 
      08-07-2007
On Mon, 06 Aug 2007 22:03:10 -0000, andrewmcdonagh
<(E-Mail Removed)> wrote, quoted or indirectly quoted someone
who said :

>Feel free to post your original method if you'd like me to show you
>what I mean using your code.


Here is the code I am trying to tidy up. Of course my mind goes blank
on all the other code I either tidied or attempted to tidy with a
similar "multiple outputs" problem.

This is a method from the static macros. It is used to expand
something like
<!-- macro Sun "JButton" docs/api/javax/swing/JButton.html -->
to
<div class="sun">Sun's Javadoc on the <span
class="jclass">JButton</span> class : available:
<ul>
<li>
<a class="offsite"
href="http://java.sun.com/javase/6/docs/api/javax/swing/JButton.html">on
the web at java.Sun.com</a></li>
<li>
<a
href="file://localhost/J:/Program%20Files/java/jdk1.6.0_02/docs/api/javax/swing/JButton.html">in
the JDK 1.6.0_02</a> or in the older <a
href="file://localhost/J:/Program%20Files/java/jdk1.5.0_12/docs/api/javax/swing/JButton.html">JDK
1.5.0_12</a> or the even older <a
href="file://localhost/J:/j2sdk1.4.2_15/docs/api/javax/swing/JButton.html">JDK
1.4.2_15</a> on your local <a class="plain" href="jdrive.html">J:
drive</a>.</li>
</ul>
</div>



/**
* guts to Generate reference to a Sun HTML document.
*
* @param fileBeingProcessed File being processed that contains
the macro to
* be expanded.
* @param desc human descriptinon of what it is.
* @param ref reference e.g
api/java/awt/package-summary.html
* /s or \s. Generates
http://java.sun.com/j2se/1.6.0/docs/...e-summary.html
*
* @return expand href both to on-line and local.
*
* @noinspection WeakerAccess
*/
public static String expand( File fileBeingProcessed,
String desc,
String ref )
{
/* chop lead / if any */
char firstChar = ref.charAt( 0 );
if ( firstChar == '/' || firstChar == File.separatorChar )
{
ref = ref.substring( 1 );
}

/** reference with / instead of File.separatorChar . partial
or complete url.
* May be extended in following code.
*/
String refWithSlashes = ref.replace( File.separatorChar, '/'
);
/**
* short description with bold html applied.
*/
final String boldDesc = "<b>" + desc + "</b>";
/**
* expanded description
*/
final String longDesc;
/**
* Are the docs also locally available in the recent JDK docs?
*
* JDK 1.6 has directories api, jdk, jre, legal, platform,
technotes
* jdk 1.5 guide*, relnotes*, tooldocs*, no longer avail.
Still has api.
*/

final boolean localRecent;
/**
* Are the docs available in old locally available JDK docs?
*/
final boolean localOld;

/**
* true on Sun's site is this a reference to the
* http://java.sun.com/javase/6/ directory.
* False, implies http://java.sun.com/
*/
final boolean javase6;

// expand forms where don't supply full URL.
refWithSlashes = expandAbbreviations( refWithSlashes );

// deal with long forms
if ( refWithSlashes.startsWith( "docs" ) )
{

if ( refWithSlashes.startsWith( "docs/books" ) )

{
// Only on sun site, not local.
javase6 = false;
localOld = false;
localRecent = false;

if ( refWithSlashes.startsWith( "docs/books/codeconv"
) )
{
longDesc = "Sun's Coding Conventions on " +
boldDesc;
}
else if ( refWithSlashes.startsWith( "docs/books/jls"
) )
{
longDesc =
"Sun's JLS (<b>J</b>ava <b>L</b>anguage
<b>S</b>pecification) on "
+ boldDesc;
}
else if ( refWithSlashes.startsWith( "docs/books/jni"
) )
{
longDesc =
"Sun's JNI (<b>J</b>ave <b>N</b>anive
<b>I</b>nterface) Specification on "
+ boldDesc;
}
else if ( refWithSlashes.startsWith(
"docs/books/tutorial" ) )
{
longDesc = "Sun's tutorial on " + boldDesc;
}

else if ( refWithSlashes.startsWith(
"docs/books/vmspec" ) )
{
longDesc =
"Sun's VM (<b>V</b>irtual <b>M</b>achine)
Specification on "
+ boldDesc;
}
else
{
longDesc = "Sun's Book on " + boldDesc;
}
}// end books

else if ( refWithSlashes.startsWith( "docs/api/" ) )
{
// strip any leading the and trailing class
desc = StringTools.removeHead( desc, "the " );
desc = StringTools.removeTail( desc, " class" );

// apply styles to class/method name
final int dotPlace = desc.indexOf( '.' );
final String decoratedDesc;
if ( dotPlace < 0 )
{
decoratedDesc =
"the <span class=\"jclass\">"
+ desc
+ "</span> class";
}
else
{
final String firstPart = desc.substring( 0,
dotPlace );
final boolean firstIsClass =
firstPart.length() <= 0 ||
Character.isUpperCase(
firstPart.charAt( 0 ) );
final String secondPart = desc.substring( dotPlace
+ 1 );
final boolean secondIsClass =
secondPart.length() <= 0 ||
Character.isUpperCase(
secondPart.charAt( 0 ) );
// don't insert words class and methods, since
obvious from dot.
decoratedDesc =
"<span class=\""
+ ( firstIsClass ? "jclass" : "jmethod" )
+ "\">"
+ firstPart
+ "</span>.<span class=\""
+ ( secondIsClass ? "jclass" : "jmethod" )
+ "\">"
+ secondPart
+ "</span>";
}
javase6 = true;
localOld = true;
localRecent = true;
longDesc = "Sun's Javadoc on " + decoratedDesc;
}

else if ( refWithSlashes.startsWith( "docs/codeconv" ) )
{
javase6 = false;
localRecent = false;
localOld = false;
longDesc = "Sun's Coding Conventions on " + boldDesc;
}
else if ( refWithSlashes.startsWith( "docs/jdk" ) )
{
javase6 = true;
localRecent = true;
localOld = false;
longDesc = "Sun's JDK Guide to " + boldDesc;
}
else if ( refWithSlashes.startsWith( "docs/jre" ) )
{
javase6 = true;
localOld = true;
localRecent = false;
longDesc = "Sun's JRE Guide to " + boldDesc;
}
else if ( refWithSlashes.startsWith( "docs/legal" ) )
{
javase6 = true;
localOld = false;
localRecent = true;

longDesc = "Sun's Legal Guide to " + boldDesc;
}
else if ( refWithSlashes.startsWith( "docs/platform" ) )
{
javase6 = true;
localOld = false;
localRecent = true;
longDesc = "Sun's JDK Platform Guide to " + boldDesc;
}
else if ( refWithSlashes.startsWith( "docs/technotes/" ) )
{
if ( refWithSlashes.startsWith(
"docs/technotes/guides/" ) )
{
javase6 = true;
localOld = false;
localRecent = true;
longDesc = "Sun's JDK Guide to " + boldDesc;
}
else if ( refWithSlashes.startsWith(
"docs/technotes/tools/" ) )
{
javase6 = true;
localOld = false;
localRecent = true;
if ( desc.endsWith( ".exe" ) )
{
longDesc =
"Sun's JDK Tool Guide to <span
class=\"exe\">"
+ desc
+ "</span>";
}
else
{
longDesc = "Sun's JDK Tool Guide to " +
boldDesc;
}
}
else
{
/** css, samples */
javase6 = true;
localOld = false;
localRecent = true;
longDesc = "Sun's JDK Technotes on " + boldDesc;
}
}// end technotes

else if ( refWithSlashes.startsWith( "docs/index.html" ) )
{
javase6 = true;
localOld = true;
localRecent = true;
longDesc = boldDesc;
}
else
{
throw new IllegalArgumentException( "Sun macro bad
reference "
+ refWithSlashes
);
}
}// end docs

else if ( refWithSlashes.startsWith( "guide/" ) )
{
// not same as technotes/guides
javase6 = false;
localOld = false;
localRecent = false;
longDesc = "Sun's Guide to " + boldDesc;
}

else if ( refWithSlashes.startsWith( "products/" ) )
{
javase6 = false;
localOld = false;
localRecent = false;
longDesc = "Sun's Product Info on " + boldDesc;
}
else if ( refWithSlashes.startsWith( "webnotes/" ) )
{
javase6 = true;
localOld = false;
localRecent = false;
longDesc = "Sun's Release notes on " + boldDesc;
}
else
{
// eg. j2ee
javase6 = false;
localOld = false;
localRecent = false;
longDesc = "Sun's documentation on " + boldDesc;
}
return buildSunLinks( refWithSlashes,
localRecent,
longDesc,
javase6,
localOld,
fileBeingProcessed );
}

extracting the giant If does not really make the code all that
clearer. Somehow breaking up the giant if should be higher priority.
--
Roedy Green Canadian Mind Products
The Java Glossary
http://mindprod.com
 
Reply With Quote
 
andrewmcdonagh
Guest
Posts: n/a
 
      08-07-2007
On Aug 7, 1:48 pm, Roedy Green <(E-Mail Removed)>
wrote:
> On Mon, 06 Aug 2007 22:03:10 -0000, andrewmcdonagh
> <(E-Mail Removed)> wrote, quoted or indirectly quoted someone
> who said :
>
> >Feel free to post your original method if you'd like me to show you
> >what I mean using your code.

>
> Here is the code I am trying to tidy up. Of course my mind goes blank
> on all the other code I either tidied or attempted to tidy with a
> similar "multiple outputs" problem.
>
>


snippetty snip

> extracting the giant If does not really make the code all that
> clearer. Somehow breaking up the giant if should be higher priority.
> --
> Roedy Green Canadian Mind Products
> The Java Glossaryhttp://mindprod.com


Hi,

I'm at work at present so will look at refactoring it later in the
day, but for now, I'd can quess where I'd start.

You are right about the big IF statement and cleanign it up. Its
certainly one of the first I'd look at.

I'd start by removing the duplication: E.g. setting of the 3 booleans
to various values, possibly replacing them with an enum, or moving the
logic and the thre booleans into its own class - thereby hiding the 3
booleans from the world, or even using polymorhpism to remove the need
for conditional checking at all.

However, when I'm confronted by code like this, I tend to look at it
from the view point of 'There's hidden classes within this method,
never mind hidden private methods'.

Hth

Andrew





 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
Refactoring a while loop without if .. else - Java 6 05-13-2005 11:29 PM
What does "refactoring" of a project mean ? Anan H. Samiti Java 33 07-30-2004 08:07 PM
Odd Multi-thread behavior when refactoring Christian Bongiorno Java 1 06-22-2004 07:46 AM
Survey on refactoring activities using IDEs Sebastian Jekutsch Java 5 06-09-2004 06:15 AM
come learn all about refactoring Refactorit Java 0 02-22-2004 06:36 PM



Advertisments