Adapter: Temperature Sensors
Problem
Three kinds of sensors report temperature in different units: celsius, fahrenheit and kelvin (all whole numbers). Every input line is: unit value. Print each reading in whole degrees Celsius. Fahrenheit to Celsius is (value - 32) * 5 / 9 rounded down; Kelvin to Celsius is value - 273. Use the adapter pattern: the main loop works only with a common sensor interface that has a celsius() method, and an adapter wraps each old sensor class that has its own method names.
Constraints
- 1 <= lines <= 1000
- |value| <= 10^6
Examples
Input
celsius 25 fahrenheit 212 kelvin 300
Output
25 100 27
Solutions
The main loop only calls celsius(). Each adapter translates this call into the method and unit of the old class. A new device type needs a new adapter and no change in the loop.
/*
* Problem : Process lines.
* Input : Several command lines.
* Output : One Celsius value per line.
* Approach: Adapter classes.
* Time : O(n)
* Space : O(1)
* Java : 8 or newer.
*/
import java.util.Scanner;
public class Main {
// Step 1: The interface that the main loop wants to use.
interface CelsiusSensor {
long celsius();
}
// Step 2: Old classes that we cannot change. Each has its own method name and unit.
static class FahrenheitDevice {
final long degrees;
FahrenheitDevice(long degrees) {
this.degrees = degrees;
}
long readFahrenheit() {
return degrees;
}
}
static class KelvinDevice {
final long degrees;
KelvinDevice(long degrees) {
this.degrees = degrees;
}
long readKelvin() {
return degrees;
}
}
// Step 3: Adapters wrap an old object and make it look like a CelsiusSensor.
static class FahrenheitAdapter implements CelsiusSensor {
private final FahrenheitDevice device;
FahrenheitAdapter(FahrenheitDevice device) {
this.device = device;
}
public long celsius() {
return Math.floorDiv((device.readFahrenheit() - 32) * 5, 9);
}
}
static class KelvinAdapter implements CelsiusSensor {
private final KelvinDevice device;
KelvinAdapter(KelvinDevice device) {
this.device = device;
}
public long celsius() {
return device.readKelvin() - 273;
}
}
// 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++) {
String[] part = lines[i].split(" ");
long value = Long.parseLong(part[1]);
CelsiusSensor sensor;
if (part[0].equals("fahrenheit")) {
sensor = new FahrenheitAdapter(new FahrenheitDevice(value));
} else if (part[0].equals("kelvin")) {
sensor = new KelvinAdapter(new KelvinDevice(value));
} else {
sensor = () -> value;
}
output[i] = String.valueOf(sensor.celsius());
}
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 (2)
- Why can the old classes stay unchanged?
- Which method does the main loop call?