Search code examples
javaoopencapsulationhelpermethods

When to create helper methods and separate files


Background: I have a large (several hundred lines) class that manages a concept based on some primitive-type data structures

long[] slist;  //list of unique patterns (related to polyominoes)
int[][][] sref;//patterns at each place [location][depth][<list of indices in slist>]

Question: The two methods that populate and update these data are going to be quite long, with handfuls of 5-20 line tasks, some shared, others unique. I probably want to make a helper method for each sub-task.

update(...){
    //do A
    //do B
    //do C
    //...
}
build(){
    //do D
    //do B
    //do E
    //...
}

The problem is if there are too many unrelated helper methods in one file, readability does not improve.

The answer to this question gets me most of the way there. I can declare the structures in the same package, in their own class, and access the primitive member field or call related methods. But I still want to know the accepted wisdom here because this organization did not come easily to mind.

Would you ever go so far as to put update() and build() functions in their own files? If so, where should their common tasks be declared?


Solution

  • I highly recommend reading Refactoring (Amazon link) by Martin Fowler; it should be in every programmer's library and will help you with situations like this. I will refer to it in the post.

    If a class has too much code, then it's usually time to split up the class. That may entail creating member variables that have classes (delegating functionality), or it may mean creating an object on the fly (replace method with method object). Things that share commonalities are good cases for applying inheritance or the state/strategy pattern.

    Short answer

    Yes, you would go so far as to have those functions in their own files. However, I would instead make them classes. Perhaps Updater and Builder objects. You can inherit from BuilderCommon and UpdaterCommon classes. These new objects will be coupled to the old object, but that's okay. You may consider putting these new sets of classes in their own package. Hierarchical organization will help with readability and reuse of common code. Try to take advantage of concepts like inheritance and abstraction techniques like generics to do the work for you. If you can find commonality between doA, doB, etc., make UpdateHelper classes out of them and put them in a list. Then simply iterate over the list.

    This is just one of the many ways to do it:

    public class Updater
    {
        public Updater(List<IUpdateHelper> helpers)
        {
            helpers = new ArrayList<UpdateHelper>();
            this.helpers.add(helpers);
        }
        public void update()
        {
            for (IUpdateHelper helper : helpers)
            {
                helper.performHelp();
            }
        }
    
        protected List<IUpdateHelper> helpers;
    }
    
    public class UpdaterCommon extends Updater
    {
        public UpdaterCommon()
        {
            helpers.add(new UpdateHelperA());
            ... // Etc.
        }
    }
    
    /*
     * This uses inheritance for common helpers, but it could just as well use
     * delegation. Also, this assumes that order of invocation for each helper 
     * doesn't matter.
     */
    public class UpdaterOne extends UpdaterCommon {...}
    
    interface IUpdateHelper
    {
        public void performHelp();
    }
    public class UpdateHelperA implements IUpdateHelper {...}
    

    Definitely replace those arrays with objects and add an interface.

    Closing

    In my experience it usually only takes the application of a few of these concepts on a regular basis to make a significant difference in code quality. If a package, class, method, conditional, etc. gets to be unruly, break it out into a smaller unit. Keep pushing the nitty-gritty functionality down into very small methods so that you can look at the code from a high level.