简体   繁体   中英

Is there ever any advantage to using static functions / variables versus a class / objects?

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.

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 (ie 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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM