Early Access: 87 spots left.

Claim
Low Level DesignSingletonSingleton: Make Logger a Single Shared Instance

Course content

Singleton: Make Logger a Single Shared Instance

Easy·Tagsdesign-patternscreationalsingleton

Problem Statement

The starter `Logger` is a regular class. Every caller that wants a logger calls `new Logger()` and gets its own private instance. Messages logged through one logger are not visible through another, even though every caller thinks it is talking to "the" logger. The `getInstance` method on the starter looks helpful but is broken. It returns a fresh `Logger` every call, so it does not actually share anything either: ```java public static Logger getInstance() { return new Logger(); } ``` Refactor `Logger` into a Singleton: exactly one shared instance, accessible through `Logger.getInstance()`, with no way for anyone to construct their own. After your refactor, the design must satisfy this contract: - `Logger.getInstance()` returns the same reference every time it is called. - `Logger`'s constructor is private. Callers cannot do `new Logger()`. - Messages logged through one `Logger.getInstance()` reference are visible through any other `Logger.getInstance()` reference, because they all point at the same object. - `getInstance` is a static method that returns a `Logger`. The validator runs five checks: 1. `Logger.getInstance() == Logger.getInstance()` evaluates to true. The behavioural Singleton test. 2. `Logger` does not have a public no-arg constructor. Reflection check, mirrors the contract that callers cannot allocate their own. 3. State is shared across `getInstance()` references. The validator logs a message through one reference, retrieves messages through another, and asserts the message is present. 4. `getInstance` is declared and is static. 5. `log` and `getMessages` work correctly when used through a single `Logger.getInstance()` reference. Smoke test against accidental damage to the basic API. The smelly starter passes scenarios 4 and 5 only. The other three require the actual refactor.

Examples

Example 1
Input
Logger a = Logger.getInstance(); Logger b = Logger.getInstance(); a == b
Output
"true"
Why
Both references point at the same shared Logger object.
Example 2
Input
Logger.getInstance().log("booting"); Logger.getInstance().getMessages()
Output
"[\"booting\"]"
Why
The message logged via one reference is visible through any other reference because there is only one Logger.
Example 3
Input
new Logger()
Output
"compile error after the refactor"
Why
The constructor is private. The compiler refuses any direct allocation, which is exactly what makes Logger a Singleton.

Constraints

  • Logger must declare a private no-arg constructor.
  • Logger must declare a public static method named getInstance() that returns Logger.
  • Successive calls to Logger.getInstance() must return the same reference.
  • Logger.log(String) must append the message to the shared message list.
  • Logger.getMessages() must return the shared message list.