There is nothing wrong with static classes that are truly static. That is to say, there is no internal state to speak of that would cause the output of the methods to change.
If Dice.roll()
is simply returning a new random number from 1 to 6, it's not changing state. Granted, you may be sharing a Random
instance, but I wouldn't consider that a change of state as by definition, the output is always going to be well, random. It is also thread-safe so there are no problems here.
You'll often see final "Helper" or other utility classes which have a private constructor and static members. The private constructor contains no logic and serves only to prevent someone from instantiating the class. The final modifier only brings this idea home that this isn't a class you'd ever want to derive from. It is merely a utility class. If done properly, there should be no singleton or other class members which are not themselves static and final.
So long as you follow these guidelines and you're not making singletons, there is absolutely nothing wrong with this. You mention a controller class, and this will almost certainly require state changes, so I would advise against using only static methods. You can rely heavily on a static utility class, but you cannot make it a static utility class.
What is considered a change in state for a class? Well, lets exclude random numbers for a second, as they're nondeterministic by definition and therefore the return value changes often.
A pure function is one which is deterministic, which is to say, for a given input, you will get one and exactly one output. You want static methods to be pure functions. In Java there are ways of tweaking behavior of static methods to hold state, but they're almost never good ideas. When you declare a method as static, the typical programmer will assume right off the bat that it is a pure function. Deviating from expected behavior is how you tend to create bugs in your program, generally speaking and should be avoided.
A singleton is a class containing static methods about as opposite of "pure function" as you can be. A single static private member is kept internally to the class which is used to ensure there is exactly one instance. This is not best practice and can get you into trouble later for a number of reasons. To know what we're talking about, here is a simple example of a singleton:
// DON'T DO THIS!
class Singleton {
private String name;
private static Singleton instance = null;
private Singleton(String name) {
this.name = name;
}
public static Singleton getInstance() {
if(instance == null) {
instance = new Singleton("George");
}
return instance;
}
public getName() {
return name;
}
}
assert Singleton.getInstance().getName() == "George"