Search code examples
design-patternssingletondice

Good example of Singleton usage


Recently I've read a lot about the Singleton pattern. As far as I'm concerned singleton objects should be used only if there is no sense to have more than one, and when there is a need to access them from all over the program. My question is fairly simple and for educational purposes. When making a board game simulator such as Monopoly or Catan is it correct to create Dices (throwable board game dices) as a singleton class?


Solution

  • The question to ask here is what value you're getting out of the Singleton behavior. You've described this as "only if there is no sense to have more than one, and when there is a need to access them from all over the program". The nuance there is that even if it wouldn't be useful to have more than one, you might choose against Singleton: It might be fine to allow multiple instances to be created if that doesn't have any significant downsides.

    Remember, if you implement Singleton well, that object cannot be destroyed throughout the lifetime of the application, and you'll need to synchronize the creation of the object so multiple threads can't create multiple instances. For heavy objects, or objects with heavy dependencies, that might actually lead to greater long-term memory usage because you cannot destroy or garbage-collect the object throughout your app's lifecycle. That leaves a gap between which objects can be Singleton and which objects should be Singleton. It'll require your judgment.

    Some factors to consider:

    1. Will your application be correct if more than one is created? For something like a DatabaseService or StorageService, the answer might be "no", in which case Singleton behavior is absolutely required.

    2. Will your application have good performance if more than one is created? For something like a WebRequestService, there may be some additional value in having one object queue or manage the requests, and that might be a good motivation to make it Singleton.

    3. If your object does not have to be Singleton, is it expensive to create, or will it be created often enough to want to reuse the object? In this case you have to weigh the expense of creation versus the expense of the Singleton. Imagine a Dictionary or SpellChecker where the results are correct even if you create a new one, but you want to minimize the number of times you have to read the dictionary file from disk. There are sometimes more options than Singleton, such as Dagger's @Reusable scope, which would give you some of the benefits with fewer costs.

    For your Dice class:

    • If a Dice class represents exactly one dice roll determined when the object is created, obviously it can't be Singleton, because then you could only roll the dice once and it would always return the same value. You probably don't want that.
    • If your Dice class represents a pure (pseudo)random number generator, it generally won't have to be Singleton: It's probably cheap to create, and there's probably no advantage to asking the same object in sequence. You could plausibly make this "reusable" or keep the Dice objects in a pool to avoid recreating them, but if I were writing the code I think it'd be unlikely for that to be worthwhile.
    • If you would like your games to be repeatable, such as to have predictable games in integration tests, it might make sense to have the Dice object be Singleton: In that case you might want the single object so it can be called in the same order and receive the same random-seeded results.
    • If you're calling out to a random number service like https://www.random.org, it may be important to make the object Singleton so it can batch, cache, and reuse those requests.

    For "Dice" I'd make that "scopeless", creating a new instance every time and allowing for its replacement in tests. In contrast, it probably makes sense for your Board, Game, or GameState object to be Singleton across the application.