Test with a Fake Clock

medium Testing and Clean Code

Problem

A Greeter must not read the real clock, because then it cannot be tested. It gets a clock as an input (dependency injection) and asks the clock for the hour. Hours 5 to 11 give Good morning, 12 to 16 Good afternoon, 17 to 20 Good evening, and all other hours Good night. Each input line is the hour that a fake clock returns. Print the greeting for each line.

Constraints

  • 0 <= hour <= 23

Examples

Input

9
13
22

Output

Good morning
Good afternoon
Good night

Solutions

Best approachTime O(n)Space O(n)

Code that reads the system time directly is hard to test, because the result changes every hour. Passing the clock in from outside lets a test control the time. The same idea is used for databases, network calls and random numbers.

Same code works in Java810172125Basic features only, so nothing changes between versions.
/*
 * Problem : Process lines.
 * Input   : Several lines.
 * Output  : One greeting per line.
 * Approach: Fake clock.
 * Time    : O(n)
 * Space   : O(n)
 * Java    : 8 or newer.
 */
import java.util.Scanner;

public class Main {
        // Step 1: The clock is an interface. A real program passes the system clock, a test passes a fake one.
        interface Clock {
            int hour();
        }

        static class Greeter {
            private final Clock clock;

            Greeter(Clock clock) {
                this.clock = clock;
            }

            // Step 2: The greeter only asks the clock. It never knows if the clock is real or fake.
            String greet() {
                int hour = clock.hour();
                if (hour >= 5 && hour <= 11) {
                    return "Good morning";
                }
                if (hour >= 12 && hour <= 16) {
                    return "Good afternoon";
                }
                return hour >= 17 && hour <= 20 ? "Good evening" : "Good night";
            }
        }

    // This method solves the task. It receives the input and returns the answer.
    static String[] solve(String[] lines) {
        String[] output = new String[lines.length];
        for (int i = 0; i < lines.length; i++) {
            // Step 3: A fake clock is just a lambda that returns the hour from the input line.
            int hour = Integer.parseInt(lines[i]);
            output[i] = new Greeter(() -> hour).greet();
        }
        return output;
    }

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        java.util.List<String> all = new java.util.ArrayList<>();
        while (scanner.hasNextLine()) {
            String line = scanner.nextLine().trim();
            if (!line.isEmpty()) {
                all.add(line);
            }
        }
        String[] lines = all.toArray(new String[0]);
        System.out.println(String.join("\n", solve(lines)));
    }
}
Hints (1)
  1. What other things should be passed in, not created inside?