Re: Large Java classes wanted

J Arrizza <[email protected]> Mon, 22 Feb 2010 21:26:50 -0800
Newsgroups gmane.comp.programming.refactoring
Message-ID <[email protected]>
On Mon, Feb 22, 2010 at 5:23 PM, Heinrich Breedt
<[email protected]> wrote:
> http://www.amazon.com/Working-Effectively-Legacy-Michael-Feathers/dp/0131177052
>

The Feathers book is very good. Here's some additional strategies to consider.

You will use two basic refactorings: Extract Method and Extract Class.
You will use Extract Method most of the time, but that's just a
stepping stone to the real big-bang-for-buck workhorse, Extract Class.

The basic thing to realize is that a very large class is a set of
smaller classes intertwined together. And that a class is a cohesive
set of methods that associate strongly with each other and associate
(very) strongly with a small set of variables.

1) The primary strategy to use is:

a) isolate one member variable of the large class and extract it into
a private class as a public variable

old:  private int mVar;

new:
   private class SomeThingClass
       {
       public int mVar;
       }

b) instantiate the new class in the large class

      SomeThingClass mNewClass = new SomeThingClass();

c) search for all lines in the class that use or modify that member
variable and replace them with calls into the new private class:

      old:    mVar++;
      new:   mNewClass.mVar++;

d) start extracting lines of code that use or modify that member
variable into the new private class:

      old : mVar++;

      new:  mNewClass.Bump();

      and in SomeThingClass:
      void Bump()
         {
         mVar++;
         }

e) after a few extractions, you will probably need to rename the class
since it's purpose should become clearer as you extract more lines.

f) check for member variables in the Large Class that are not
standalone but are attributes of other member variables (for an
analogy think "Third Normal Form"). Eg.

     string[] mAppleTypes;          // holds data
     int mNumberOfAppleTypes;  // holds information about mAppleTypes,
i.e. it's an attribute

These two variables should be extracted out together.

2) You probably don't have unit tests in place. So add some -- any --
testing, whatever you can manage, it doesn't have to be pretty. They
must be automated but they don't have to test at the unit level.

3) No unit tests mean high risk. So minimize the risk by looking for
ways to simplify the large class by doing very small, very simple
extractions. Typically look for 2 - 5 lines of code you can extract
out into a void-void method:

     ...
     stmt1;
     stmt2;
     stmt3;
     ...

replaced by:

     ...
     SomeGoodFunctionName();
     ...

     private void SomeGoodFunctionName()
         {
        stmt1;
        stmt2;
        stmt3;
         }

The key is to find statements that are tightly coupled for some
reason, e.g. they all act on a single member variable, they perform a
very simple, but atomic (can't be reduced any further) kind of action,
etc.

By the way, most programmers consciously or sub-consciously use the
"white space" around statements to set them off (and some even put a
comment or two above them). This is a coarse way to look for void-void
sets of statements, but believe it or not, does work well since you
are piggybacking on top of previous developer's understanding of
"cohesion".

Once you have a whole set of these smaller functions, look for
commonality amongst them. If two or more functions all act on a single
member variable or they all modify/use a small number of member
variables, extract them out into their own class.

4) Look for methods in the Large Class that repeat the same formal
variables in their signatures. Extract these out into their own class.
The methods of the new class are the old methods without the formal
variables, the member variables are the old formal variables.  E.g.

old:
    void fnA(int val1, int val2, int val3);
    void fnB(int val1, int val2, int val3);

new:

     private class SomeNewClass
        {
        private int val1;
        private int val2;
        private int val3;

        public SomeNewClass(int v1, int v2, int v3)
            {
            val1 = v1;
            val2 = v2;
            val3 = v3;
            }

         public void fnA()  { ... }
         public void fnB()  { ... }
        }

Why? because formal variables are tightly coupled to the method that
uses them. If several methods use the same formal variables, then
those methods are most likely tightly coupled to each other (i.e. they
form a class).

5) Repeat 1 - 4 as often as you can. My experience has been that the
original Large Class pretty much disappears leaving only a vestige of
itself (just like the Chesire Cat).

6) As the primary purpose of the extracted classes becomes clearer,
start adding Unit Tests for them that reinforces the intent of the new
class. That actually helps clarify their purpose much further and may
lead to a splitting of that class again (this is a good thing!).

7) Checkpoint the source files often. You will probably have to
backtrack when you go down the wrong rabbit hole and so you'll need a
quick way of going back to a known good point.

Not glamorous work, but does pay off. I had one chunk of code that
started off as 60,000 lines of VB and C++ code, boiled it down to
12,000 over several months. Lot of duplicate code disappeared, a lot
of dead code was deleted, and my personal favorite, useless code was
deleted. Useless code is code that is executed, but never actually
changes the state of the system (yes it can change variables but those
variables aren't significant to the system's purpose, so they can be
deleted along with all the code that acted on them).

John


------------------------------------

Yahoo! Groups Links

<*> To visit your group on the web, go to:
    http://groups.yahoo.com/group/refactoring/

<*> Your email settings:
    Individual Email | Traditional

<*> To change settings online go to:
    http://groups.yahoo.com/group/refactoring/join
    (Yahoo! ID required)

<*> To change settings via email:
    [email protected] 
    [email protected]

<*> To unsubscribe from this group, send an email to:
    [email protected]

<*> Your use of Yahoo! Groups is subject to:
    http://docs.yahoo.com/info/terms/