Question

I've recently begun coding in Java in the past few months. I have a Matrix class that's becoming much too bloated with a lot of methods. I also have a SquareMatrix class that extends Matrix, and reduces some of the bloat.

What I've found is that alot of the methods in the Matrix class are relevant to matrices in general. It has all the basics, like addMatrix(Matrix), multiplyMatrix(Matrix), multiplyMatrix(float), then more complex methods like getGaussian(Matrix) or getLUDecomposition(Matrix).

What are my options in reducing the number of lines in my class Matrix? Is it normal for a class to become extremely large? I don't think so... Is this something I should have considered early on and refactoring is difficult? Or are there simple solutions?

Thank you!


Edit: After reading a couple of responses, I'm considering doing the following:

Utility class Matrix contains all common/basic methods for a matrix

Delegation classes (Helper/sub):

*Helper Class* getGaussian(..), getLUFactorization(..), ...

Subclasses (extends Matrix) SquareMatrix, LowerTriMatrix, UpperTriMatrix, ...

Delegation seems similar to defining multiple .cpp files with headers in C++. More tips are still welcome.


Edit2: Also, I'd prefer to refactor by properly designing it, not quickfixes. I hope it helps down the road for future projects, not just this one.

Was it helpful?

Solution

Interfaces should be both complete and minimal, meaning that a type interface should contain all methods necessary to achieve the needed and meaningful tasks on that type, and only these.

So analyse the API methods of Matrix and determine which of them belong to the core API (which must have access to the internals of the class to accomplish their task), and which are providing "extended" functionality. Then reduce the class API to the core functionality, migrating the rest of the methods to separate helper / utility / subclasses which can accomplish their goals using the public core API.

For refactoring / unit testing help, consider getting Working Effectively with Legacy Code by Michael Feathers.

OTHER TIPS

Refactoring is definitely a good thing, but it's difficult to refactor reliably unless you have good unit tests. Test-driven development offers a way to achieve a codebase that is usually easy to refactor.

For existing code without unit tests, this means that before you need to first isolate the area to be refactored, write unit tests first, and then refactor. If this is for just a personal project though, you can probably get away by writing tests for just the minimal set of features you want to survive the refactor intact.

Regarding how to go about the refactoring you have at hand, there are several principles and methods like some other answers have noted. What's worked for me in most cases is:

Strictly enforce the following style rules:

  1. Methods cannot be longer than 40 lines
  2. Class fan-out cannot be more than 20

There are others, but I personally found these to be most helpful in steering myself away from bad design. Limiting method length forces you to really think about method signatures and write concise, focused methods. This usually makes duplication easier to spot and remove. Limiting class fan-out has a similar effect at the class level, it forces you to think hard and about the class's role and make it concise.

Tools such as checkstyle can enforce both of these, along with a bunch of other style rules.

Both Eclipse and IDEA IntelliJ provide an "extract method" refactor, which makes it as simple as highlighting the code you want to extract and hitting a shortcut key. IntelliJ even checks for occurrences of the exact same code in the class and replaces them with a call to the new method as well. In fact I'd recommend using such refactoring tools all the time over doing a manual copy-paste and edit.

Quick and dirty way to have your bloated class clean-looking (by moving the mess elsewhere)

You could put the contents of your methods in static methods of an external utility class.

Example:

Matrix.java

import MatrixUtils;
//[...]
public class Matrix
{
    //[...]
    Matrix()
    {
        //[...]
    }
    //[...]
    public Gaussian getGaussian(Matrix m)
    {
        return MatrixUtils.computeGaussian(m);
    }
    public LUDecomposition getLUDecomposition(Matrix m)
    {
        return MatrixUtils.computeLUDecomposition(m);
    }
}

MatrixUtils.java

protected class MatrixUtils
{
    private MatrixUtils()//Utility class: prevent instantiation
    {//nothing
    }
    //[...]
    protected static Gaussian computeGaussian(Matrix m)
    {
        //do your superlong 100+lines Algorithm and return the result
    }
    public static LUDecomposition computeLUDecomposition(Matrix m)
    {
        //do your superlong 100+lines Algorithm and return the result
    }
}

This way (quick and dirty, as I said) you can browse cleanly through your Matrix class (especially when you have lots of methods and javadoc).

When you want to access the actual code you go to MatrixUtils.

If, for some reason, each method is super-uber-long (200+ lines of code!?) AND if, for some other reason, you really need to keep your .java files short, you could even create a "static class" (i.e. an utility class with static methods only) for every [uber-long] method.

...and of course you should in any case do, to some degree, "procedural" refactoring, as mentioned by @oksayt, where you shorten your methods by putting some blocks of code into separate (yet coherent) functions.

What does it mean "extremely large"?

I think that method should not be longer than 100 lines and class should not exceed 1000 lines including javadoc. If you need more, check your design. Use delegation, i.e. separate your logic into modules/classes and call one class from another.

For basic matrix operations you could use an open source implementation with a suitable license. You could consider:-

http://math.nist.gov/javanumerics/jama/

http://code.google.com/p/efficient-java-matrix-library/

http://commons.apache.org/math/

This would definitely reduce your overall code size and you get the benefit of more tested, used code base.

Hope this helps.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top