6

I remember a couple years ago I was using static initializers to call class-level setup operations. I remember it having very bizarre behaviors and I just decided to steer clear from them. Maybe it was because I was messing up the top-bottom order or being a newbie. But I am encountering a need to revisit them and I want to make sure there is not a better way that is just as concise.

I know it is not fashionable, but I often have data-driven classes that maintain a static list of instances imported from a database.

public class StratBand { 
      private static volatile ImmutableList<StratBand> stratBands = importFromDb();

      private final int minRange;
      private final int maxRange;

      private static ImmutableList<StratBand> importFromDb() { 
            //construct list from database here
      }
      //constructors, methods, etc
}

When I have dozens of table-driven classes like this one, this pattern is very concise (yes I know it tightly couples the class with one source of data/instances).

However, when I discovered the goodness of Google Guava I want to use the EventBus to update the static list when a certain event posted. I would create a static final boolean variable just to call a static method that initialized the registration.

public class StratBand { 
      private static volatile ImmutableList<StratBand> stratBands = importFromDb();
      private static final boolean subscribed = subscribe();

      private final int minRange;
      private final int maxRange;

      private static ImmutableList<StratBand> importFromDb() { 
            //construct list from database here
      }
      //constructors, methods, etc

      private static boolean subscribe() {
            MyEventBus.get().register(new Object() { 
                @Subscribe
                public void refresh(ParameterRefreshEvent e) { 
                    stratBands = importFromDb();
                }
            });
        return true;
      }
}

This got annoying very quickly, because the compiler would throw warnings over the subscribed variable never being used. Also, it just added clutter. So I'm wondering if it is kosher to use the static initializer, and there really is no better way if I do not decouple this into two or more classes. Thoughts?

 public class StratBand { 
          private static volatile ImmutableList<StratBand> stratBands = importFromDb();

          static { 
           MyEventBus.get().register(new Object() { 
                    @Subscribe
                    public void refresh(ParameterRefreshEvent e) { 
                        stratBands = importFromDb();
                    }
                });
          }

          private final int minRange;
          private final int maxRange;

          private static ImmutableList<StratBand> importFromDb() { 
                //construct list from database here
          }
          //constructors, methods, etc


    }
tmn
  • 9,565
  • 10
  • 49
  • 99
  • 2
    It's "not fashionable" precisely because, as you discovered, it does result in "very bizarre behaviors." It's generally a better practice not to have these things static at all, to pass them where they're needed explicitly (or implicitly with dependency injection). Static initializers are fine when they're just creating singletons, or doing pure computation, but they generally shouldn't interact with the filesystem, databases, or anything outside, really. – Louis Wasserman Apr 06 '15 at 18:28
  • I was waiting for someone to say that. And I get the DI paradigm which I appreciate. But until we are ready to scale up to a DI-driven framework, I'm kind of hoping to maintain what we have a little longer. – tmn Apr 06 '15 at 18:41
  • 1
    You don't need to use DI here, but I would be surprised if you could get this to work at all, and it's going to remain extremely fragile, difficult to test, and frankly it'd take _more_ effort than doing it properly would take. – Louis Wasserman Apr 06 '15 at 18:43
  • On that note I will probably avoid the static initializer then. – tmn Apr 06 '15 at 20:30

1 Answers1

3

So I'm wondering if it is kosher to use the static initializer

The funny thing is that

private static final boolean subscribed = subscribe();

and

private static final boolean subscribed;
static {
    subscribed = subscribe();
}

get compiled to exactly the same bytecode. So using the needless static variable is strictly worse.


But until we are ready to scale up to a DI-driven framework,

Discover Guice. Don't call it framework (though it is). It's easy to use and let's you get rid of static.

Or do it manually. Rewrite your class by dropping all static modifiers and pass it everywhere you need it. It's rather verbose sometimes, but stating dependencies explicitly allows you to test classes in isolation.

The way it is, you can't test StratBand without hitting the database, no matter how trivial the method under test is. The problem is the coupling of every StratBand instance to the list of all StratBands.

Moreover, you can't test the behavior dependent on the stratBands contents as it always get loaded from the DB (sure, you can fill your DB correspondingly, but it's a big pain).

For starters, I'd create StratBandManager (or StratBands or whatever name you like) and move all the static functionality to it. In order to easy the transition, I'd create a temporary class with static helpers like

private static StratBandManager stratBandManager = new StratBandManager();
public static ImmutableList<StratBand> stratBands() {
   return stratBandManager.stratBands();
}

Then deprecate it all and replace it by DI (using Guice or doing it manually).


I find Guice useful even for small projects. The overhead is tiny as often there's no or hardly any configuration.

Community
  • 1
  • 1
maaartinus
  • 40,991
  • 25
  • 130
  • 292
  • Yeah I have spent many hours studying Guice, although I have not applied it yet and would like to. Given our current business environment and our function, we took the minimalist approach... and by minimalist I mean consolidating and reducing the number of classes if it simplified things. I will definitely need to consider your transition suggestion, as the community seems pretty adamant about decoupling and using DI. – tmn Apr 07 '15 at 13:07
  • @ThomasN. I wouldn't minimize the number of classes. You may want to use nested classes for the "managers" in order to keep the number of files low, but additional classes are essentially free (except for the memory cost which may matter on Android). – maaartinus Apr 07 '15 at 19:49
  • Yeah I've been fond of nested classes, because it keeps everything contained and in one place. I do realize it can cause strain maintainability when a certain threshold of complexity is passed.I think I'm going to evolve my approach a bit and start leveraging package-private paradigms more, as well approach DI-friendly designs. – tmn Apr 08 '15 at 15:17