3

In the book "Clean Code" by Robert C. Martin, he cleans up a messy class and ends with a file comprised of static variables and static functions in this fashion.

public class PrimeGenerator{
    private static int[] primes; 
    private static ArrayList<Integer> multipleOfPrimes;

    public static int[] generate(int n){
        primes = new int[n];
        //call functions
        someFunctionThatModifiesPrimes()

        return primes;
    }

    private static void someFunctionThatModifiesPrimes(){
        //modify primes
        prime[x] = y;
    }
}

He writes

Notice that it is not meant to be instantiated as an object. The class is just a useful scope in which variables can be declared and hidden.

My question is, why would I ever do what he did when I can do this:

public class PrimeGenerator{
    private int[] primes; 
    private ArrayList<Integer> multipleOfPrimes;

    public PrimeGenerator(int n){
        primes = new int[n];
    }

    public int[] generate(int n){
        //call functions
        someFunctionThatModifiesPrimes()

        return primes;
    }

    private void someFunctionThatModifiesPrimes(){
        //modify primes
        prime[x] = y;
    }
}

His code:

a) Is not thread safe, calling 'generate(int)' while primes are already generating (calling from multiple threads) would make this fail.

b) Keeps a global variable with garbage primes after it finishes running, which just get override next time it runs.

The only advantage that I can think of is that it might be faster? And even then that would be negligible.

The code that requires object creation is thread safe, does not hold on to garbage data and does not have a static state.

WinterDev
  • 348
  • 1
  • 11
  • 1
    Yeah, lack of thread safety is a common theme in Martin's *Clean Code*. Probably a consequence of the age of the book. People used to not care about this stuff (which was foolish, even back then, and led to many problems in legacy codebases). – Cody Gray - on strike Sep 11 '17 at 03:10
  • @CodyGray Even without considering thread safety, I have a hard time understanding how that style of code would ever be considered good or clean compared to the alternative. He obviously is capable of wrapping things into class / objects considering the same example creates a class for RowColumnPagePrinter, so why does he randomly use 'static' this time? It can't be random, but he doesn't seem to explain his reasoning in the text following the example. – WinterDev Sep 11 '17 at 03:16
  • I would guess its to just make it easier to access, this way you do not need to create an object in order to generate and read the primes. Its a way to package all the variables and functions that are intended for one purpose into a class as well as avoiding the sort of clunky syntax of needing to call everything from an object – Mitch Sep 11 '17 at 03:23
  • @Mitchel0022 That seems like a slippery slope since everything could be considered easier to access if it's static, but for most situations, it results in horrible design. Making an object and calling it does not seem to violate any rule in the book, and he does it for plenty of other things that could also be static if that was the case. Making simple things into their own class seems to be a main point of the book, so I find it odd he doesn't do it here. – WinterDev Sep 11 '17 at 03:28
  • His intention might be that `primes` are unique mathematical values; once computed, there is no point (and it is expensive) re-computing them. – Elliott Frisch Sep 11 '17 at 03:31
  • https://stackoverflow.com/questions/2671496/java-when-to-use-static-methods explains the static vs public/private dilemma well – Mitch Sep 11 '17 at 03:32
  • @ElliottFrisch, In that case, wouldn't the code in generate have a mechanism to determine if it's already generated and return that? The way the code is written, it has to be re-calculated every time. Even considering that to be the case, I would argue keeping a static cache of the primes and using an instance for the other variables / method would be appropriate. – WinterDev Sep 11 '17 at 03:36
  • No @WinterDev you want each caller to get a different array otherwise someone changing "his" copy the list would actually be changing everybodys! But rather than regenerate the list every time, it should be faster to regenerate it only when somebody asks for more primes than we already have, and then always hand out a copy of the stored list... – Kevin Anderson Sep 11 '17 at 03:41
  • @KevinAnderson A new array is generated each call either way, replacing the previous reference. So why maintain the array as a field, rather than pass to `someFunctionThatModifiesPrimes` as an argument, then return the local copy of the array? – Vince Sep 11 '17 at 03:48
  • @KevinAnderson So the proper method would be to give everyone a copy of the array? So that case, we would need to add a mutex of some sort to keep it thread-safe but still keep it as a static class? I guess that makes some sense – WinterDev Sep 11 '17 at 03:49
  • @VinceEmigh just figuring it's quicker to make a copy of an already computed array, if the already computed array will do, than to recompute the primes that we've already computed... – Kevin Anderson Sep 11 '17 at 03:50
  • @KevinAnderson But there is no copying in the example. The first thing that occurs is changing the reference, keeping no reference to the previous array. – Vince Sep 11 '17 at 03:51
  • Yes @VinceEmigh, as things are with the list of primes being recomputed for each call of `generate`, that static field `primes` is completely useless, made necessary only by the poor design of `someFunctionThatModifiesPrimes()`. – Kevin Anderson Sep 11 '17 at 04:09
  • In some cases, this approach does make sense. Think about the `Math` class in Java for example. It would be very tedious if you would have to create a new instance every time you want to use a function of this class. – Simon Schiller Sep 11 '17 at 07:18

1 Answers1

1

I would think focusing on the narration of one book might be an issue here. Really, it's about getting a consensus from the community as a whole to find out the best coding practices. Figure out what works better in production environments as well.

There are reasons to do static and non-static functions and variables. I would hesitate to ever use the language 'advantage', however.

Static

In cases where you have pure logic which has no bearing on member fields (i.e. the member fields are never referenced), then it would probably be better for it to be static. Basically, the reason you might do static in this cases is 'separation of concerns', in a way - if you're debugging the code, or trying to read the code and you know it's a static function, then you don't have to worry about the members.

Sometimes I make variables themselves static, and make them members of the class instead of the object. Cases like this, might in fact, be your prime numbers example. Prime numbers will not change from one implementation of PrimeGenerator to another implementation, so why would you want to calculate it twice? You wouldn't. And after calculating the values, you store them off in a way that all instantiations of the object can access, or an outside caller can access statically.

I think the call against thread saftey is a huge copout. Why can't it be threadsafe? You can use any combination of synchronized(PrimeGenerator.class), java.util.concurrent.*, or Collections.unmodifiable*(*)to make it threadsafe.

With that in mind, there is a fine line between something that is global/constant and something that is scoped to an object that is sparsely instantiated. In fact, revisiting the prime numbers example, its easy to see how that could be considered a constant(s) (there are infinite primes, but computationally there are finite, meaning we could get the complete Set as we define it to be complete). A constant is always static, just because there is no reason to waste the space on an Object for this. So really, the core of this is that the prime number example is actually a really poor example by the writer of that book. So, what would be a good example of something that isn't a constant but would be a static?

The example that comes to mind immediately is a java.util.logging.Logger, but I'll skip that because trying to argue that it's not a constant might be harder based on the argument I gave against the prime numbers. Instead, I'll go with an java.util.concurrent.atomic.AtomicBoolean, if you'll bear with me for a moment. Consider a scenario where multiple instantiations of a class Foo are doing some sort of work that all are strictly based on some AtomicBoolean, called bar, whereby they need to check the status of bar before attempting to do some work. You might do something like the observer pattern, and notify all instantiations of Foo, sure, but then you waste cpu cycles on the notifications (however much that ends up being) and at the same time, each instantiation needs to store that value off, duplicated, same as every other instantiation. Or, you could just have a public static final AtomicBoolean bar = new AtomicBoolean();, and each instantiation just needs to do Foo.bar.compareAndSet(expected, newValue);, for instance.

Non-static

At the end of the day it is kind of preference, but there is some community sprinkled in there, along with some efficiency concerns. If you really wanted to never use a static, you absolutely could. You might end up instantiating a class solely to get a value from a function, and then dumping the Object you just created on the heap, but you could do it.

Some of the recent arguments I've heard against static that I think is a more serious contender for an argument (but still really lacks any weight all the same) is that the code is not testable. In the production world, JUnits are a big deal, I'll say. Testable code catches bugs early, and yada yada yada. So what does this have to do with static? Well, out of the box, frameworks like Mockito (which is a very popular one) don't support mocking static out of the box. So a lot of very thin arguments like to claim that this means static code isn't testable.

This isn't at all true though, because all mocking frameworks support static mocking to some degree or another. Mockito itself even has an API which is meant to integrate with other frameworks, such as PowerMock. So while it is technically accurate to say Mockito can't mock statics, it is a bit misleading because it has specifically set up a way to integrate in a way where it can.

There are some minor caveats in there about how the mocking frameworks use Classes and interact with a Classloader, but I feel like that's a bit out of scope. (And I hesitate to say out of scope because it does give a little more weight to non-static and makes it seem like I'm arguing for static, which I'm not, but there is a lot to talk about here - which involves classloaders, mocking frameworks, Java Byte Code, etc etc - which is way more and out of scope of this question)

Conclusion (?)

The long winded approach is, there is nothing wrong with statics. Don't use them when you have a stateful object, and you'll be fine. Take care with threadsafety in a static context (which you should be doing in a non-static context too, so this shouldn't be a new concept), and don't break coding conventions. I know that sounds a bit generic, but really that's about all there is to it. The people I've found touting that statics are bad tend to have no real solid objections to it and it's usually about a personal ideology they want to force on the world for no good reason. So it really comes down to listening to the arguments against it and seeing if they hold weight. If they don't, then it's your own personal judgement. In the case of the book you quoted some of, I'd say that argument holds no weight.

searchengine27
  • 1,449
  • 11
  • 26