18

I'm working on a larger solo project and right now, and I have several classes in which I do not see any reason to create an instance of.

My dice class right now, for example, stores all of its data statically and all of its methods are static too. I don't need to initialize it because when I want to roll the dice and get a new value, I just use Dice.roll().

I have several similar classes that only have one main function like this and I'm about to start working on a sort of "controller" class that will be in charge of all of the events (like when a player moves, and what current turn it is) and I've found that I could follow the same idea for this class. I don't ever plan on creating multiple objects for these specific classes, so would it be a bad idea to make them fully static?

I was wondering if this is considered "bad practice" when it comes to Java. From what I've seen, the community seems to be kind of split on this topic? Anyways, I would love some discussion on this and links to resources would be great too!

YianniW
  • 191

4 Answers4

24

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"
Robert Harvey
  • 200,592
Neil
  • 22,848
11

To give an example of the limitations of a static class, what if some of your gamers want to get a slight bonus on their die rolls? And they are willing to pay big bucks! :-)

Yes, you could add another parameter, so Dice.roll(bonus),

Later you need D20s.

Dice.roll(bonus, sides)

Yeah, but some players have the "supremely capable" feat so they can never "fumble" (roll a 1).

Dice.roll(bonus, sides, isFumbleAllowed).

This is getting messy, is it not?

user949300
  • 9,009
4

In the particular case of a Dice class I think using instance methods rather than statics will make testing significantly easier.

If you want to unit test something that uses an instance of Dice (e.g. a Game class) then from your tests you can inject some form of test double of Dice that always returns a fixed sequence of values. Your test can check that the game has the right outcome for those dice rolls.

I'm not a Java developer but I think it would be significantly harder to do that with a fully static Dice class. See https://stackoverflow.com/questions/4482315/why-does-mockito-not-mock-static-methods

bdsl
  • 3,924
0

This is actually known as the Monostate pattern, where every instance (and even a "no-instance") is sharing their status. Every member is a class member (i.e., no instance members). They're commonly used to implement "toolkit" classes that bundle a set of methods and constants related to a single responsibility or requirement but don't need a state in order to work (they're purely functional). In fact, Java comes with some of them bundled (for instance, Math).

A bit off-topic: I rarely agree with the naming of keywords in VisualBasic, but in this case, I think shared is certainly clearer and semantically better (it is shared among the class itself and all of its instances) than static (remains after the lifecycle of the scope it's declared in).