diff --git a/src/main/java/ic/doc/CachedWeatherForecaster.java b/src/main/java/ic/doc/CachedWeatherForecaster.java new file mode 100644 index 0000000..dc79bc9 --- /dev/null +++ b/src/main/java/ic/doc/CachedWeatherForecaster.java @@ -0,0 +1,27 @@ +package ic.doc; + +import java.util.HashMap; + +// This class is a decorator for WeatherForecaster that caches all results. +public class CachedWeatherForecaster implements WeatherForecaster { + private final WeatherForecaster forecaster; + private final HashMap cache = new HashMap<>(); + + CachedWeatherForecaster(WeatherForecaster forecaster) { + this.forecaster = forecaster; + } + + @Override + public WeatherForecast forecastFor(WeatherRegion region, Weekday day) { + CacheKey key = new CacheKey(region, day); + WeatherForecast forecast = cache.get(key); + if (forecast == null) { + forecast = forecaster.forecastFor(region, day); + cache.put(key, forecast); + return forecast; + } + return forecast; + } + + private record CacheKey(WeatherRegion region, Weekday day) {} +} \ No newline at end of file diff --git a/src/main/java/ic/doc/CachedWeatherForecasterBuilder.java b/src/main/java/ic/doc/CachedWeatherForecasterBuilder.java new file mode 100644 index 0000000..4536950 --- /dev/null +++ b/src/main/java/ic/doc/CachedWeatherForecasterBuilder.java @@ -0,0 +1,20 @@ +package ic.doc; + +public class CachedWeatherForecasterBuilder { + private WeatherForecaster forecaster; + + private CachedWeatherForecasterBuilder() {} + + public static CachedWeatherForecasterBuilder cachedWeatherForecaster() { + return new CachedWeatherForecasterBuilder(); + } + + public CachedWeatherForecasterBuilder withForecaster(WeatherForecaster forecaster) { + this.forecaster = forecaster; + return this; + } + + public CachedWeatherForecaster build() { + return new CachedWeatherForecaster(forecaster); + } +} diff --git a/src/test/java/ic/doc/CachedWeatherForecasterTest.java b/src/test/java/ic/doc/CachedWeatherForecasterTest.java new file mode 100644 index 0000000..7ad733a --- /dev/null +++ b/src/test/java/ic/doc/CachedWeatherForecasterTest.java @@ -0,0 +1,58 @@ +package ic.doc; + +import org.jmock.Expectations; +import org.jmock.integration.junit4.JUnitRuleMockery; +import org.junit.Rule; +import org.junit.Test; + +import static ic.doc.CachedWeatherForecasterBuilder.cachedWeatherForecaster; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + +// Unit test for CachedWeatherForecaster decorator +public class CachedWeatherForecasterTest { + @Rule + public JUnitRuleMockery context = new JUnitRuleMockery(); + + private final WeatherForecaster forecaster = context.mock(WeatherForecaster.class); + private final CachedWeatherForecasterBuilder cachedWeatherForecaster = + cachedWeatherForecaster().withForecaster(forecaster); + + @Test + public void forecastForInitialCall() { + WeatherForecaster cachedForecaster = cachedWeatherForecaster.build(); + WeatherForecast expForecast = new WeatherForecast("Sunny", 20); + context.checking(new Expectations() {{ + oneOf(forecaster).forecastFor(WeatherRegion.LONDON, Weekday.MONDAY); + will(returnValue(expForecast)); + }}); + WeatherForecast actForecast = cachedForecaster.forecastFor( + WeatherRegion.LONDON, Weekday.MONDAY + ); + assertEquals(expForecast, actForecast); + } + + @Test + public void forecastForCachedCall() { + WeatherForecaster cachedForecaster = cachedWeatherForecaster.build(); + WeatherForecast expForecast = new WeatherForecast("Sunny Spells", 30); + WeatherForecast badForecast = new WeatherForecast("Rainy", 10); + context.checking(new Expectations() {{ + // Only one call, second must hit cache + oneOf(forecaster).forecastFor(WeatherRegion.LONDON, Weekday.MONDAY); + will(returnValue(expForecast)); + // Also make some other requests in-between + oneOf(forecaster).forecastFor(WeatherRegion.LONDON, Weekday.TUESDAY); + will(returnValue(badForecast)); + oneOf(forecaster).forecastFor(WeatherRegion.WALES, Weekday.MONDAY); + will(returnValue(badForecast)); + }}); + cachedForecaster.forecastFor(WeatherRegion.LONDON, Weekday.MONDAY); + cachedForecaster.forecastFor(WeatherRegion.LONDON, Weekday.TUESDAY); + cachedForecaster.forecastFor(WeatherRegion.WALES, Weekday.MONDAY); + WeatherForecast actForecast = + cachedForecaster.forecastFor(WeatherRegion.LONDON, Weekday.MONDAY); + assertEquals(expForecast, actForecast); + } +} \ No newline at end of file