From ca7e278e930c277c704c15072b12073400997d57 Mon Sep 17 00:00:00 2001
From: Colin DAMON <cdamon@ippon.fr>
Date: Fri, 18 Jun 2021 10:09:50 +0200
Subject: [PATCH] Java memoizers implementation

---
 .gitlab-ci.yml                                |  1 +
 java-memoizers/.gitlab-ci.yml                 | 11 ++++
 java-memoizers/pom.xml                        | 29 +++++++++
 java-memoizers/readme.md                      |  9 +++
 .../java/fr/craft/memoizer/Memoizers.java     | 42 +++++++++++++
 .../java/fr/craft/memoizer/MemoizersTest.java | 63 +++++++++++++++++++
 readme.md                                     |  1 +
 7 files changed, 156 insertions(+)
 create mode 100644 java-memoizers/.gitlab-ci.yml
 create mode 100644 java-memoizers/pom.xml
 create mode 100644 java-memoizers/readme.md
 create mode 100644 java-memoizers/src/main/java/fr/craft/memoizer/Memoizers.java
 create mode 100644 java-memoizers/src/test/java/fr/craft/memoizer/MemoizersTest.java

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 97907d02..0c16d1af 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -39,3 +39,4 @@ include:
   - local: "/java-monades/.gitlab-ci.yml"
   - local: "/java-diamond/.gitlab-ci.yml"
   - local: "/diamond-ts/.gitlab-ci.yml"
+  - local: "/java-memoizers/.gitlab-ci.yml"
diff --git a/java-memoizers/.gitlab-ci.yml b/java-memoizers/.gitlab-ci.yml
new file mode 100644
index 00000000..bf70e5c6
--- /dev/null
+++ b/java-memoizers/.gitlab-ci.yml
@@ -0,0 +1,11 @@
+package-java-memoizers:
+  variables:
+    PROJECT_FOLDER: "java-memoizers"
+  extends: .java
+  only:
+    refs:
+      - master
+      - merge_requests
+    changes:
+      - ".gitlab-common-ci.yml"
+      - "java-memoizers/**/*"
diff --git a/java-memoizers/pom.xml b/java-memoizers/pom.xml
new file mode 100644
index 00000000..04edddc2
--- /dev/null
+++ b/java-memoizers/pom.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <version>1.0.0</version>
+    <groupId>fr.ippon.kata</groupId>
+    <artifactId>java-parent</artifactId>
+    <relativePath>../java-parent</relativePath>
+  </parent>
+
+  <version>1.0.0-SNAPSHOT</version>
+  <artifactId>java-memoizers</artifactId>
+
+  <name>JavaMemoizers</name>
+
+  <developers>
+    <developer>
+      <email>arey@ippon.fr</email>
+      <name>Anthont REY</name>
+    </developer>
+    <developer>
+      <email>cdamon@ippon.fr</email>
+      <name>Colin DAMON</name>
+    </developer>
+  </developers>
+</project>
diff --git a/java-memoizers/readme.md b/java-memoizers/readme.md
new file mode 100644
index 00000000..bd80d3bf
--- /dev/null
+++ b/java-memoizers/readme.md
@@ -0,0 +1,9 @@
+# Java memoizer
+
+Implémentation de memoizers en Java
+
+-   **Auteurs** : Anthony REY et Colin DAMON
+-   **Date** : 18/06/2021
+-   **Langage** : Java
+-   **Niveau** : Enervé
+-   **Replay** : [Twitch](https://www.twitch.tv/videos/1059837117)
diff --git a/java-memoizers/src/main/java/fr/craft/memoizer/Memoizers.java b/java-memoizers/src/main/java/fr/craft/memoizer/Memoizers.java
new file mode 100644
index 00000000..52c14a61
--- /dev/null
+++ b/java-memoizers/src/main/java/fr/craft/memoizer/Memoizers.java
@@ -0,0 +1,42 @@
+package fr.craft.memoizer;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.function.Function;
+import java.util.function.Supplier;
+
+public final class Memoizers {
+
+  private Memoizers() {}
+
+  public static <Result> Supplier<Result> of(Supplier<Result> supplier) {
+    return () -> of(dummy -> supplier.get()).apply(null);
+  }
+
+  public static <Input, Result> Function<Input, Result> of(Function<Input, Result> function) {
+    return new MemoizedFunction<>(function);
+  }
+
+  private static class MemoizedFunction<Input, Result> implements Function<Input, Result> {
+
+    private final Function<Input, Result> function;
+    private final Map<MemoizedInput<Input>, MemoizedResult<Result>> results = new ConcurrentHashMap<>();
+
+    public MemoizedFunction(Function<Input, Result> function) {
+      this.function = function;
+    }
+
+    @Override
+    public Result apply(Input input) {
+      return results.computeIfAbsent(new MemoizedInput<>(input), this::toMemoizedResult).result();
+    }
+
+    private MemoizedResult<Result> toMemoizedResult(MemoizedInput<Input> input) {
+      return new MemoizedResult<>(function.apply(input.input()));
+    }
+
+    private static record MemoizedInput<Input>(Input input) {}
+
+    private static record MemoizedResult<Result>(Result result) {}
+  }
+}
diff --git a/java-memoizers/src/test/java/fr/craft/memoizer/MemoizersTest.java b/java-memoizers/src/test/java/fr/craft/memoizer/MemoizersTest.java
new file mode 100644
index 00000000..856cca95
--- /dev/null
+++ b/java-memoizers/src/test/java/fr/craft/memoizer/MemoizersTest.java
@@ -0,0 +1,63 @@
+package fr.craft.memoizer;
+
+import static org.assertj.core.api.Assertions.*;
+
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.function.Function;
+import java.util.function.Supplier;
+import org.junit.jupiter.api.Test;
+
+class MemoizersTest {
+
+  @Test
+  void shouldGetFunctionResult() {
+    Function<Double, Double> memoizer = Memoizers.of(d -> d * d);
+
+    assertThat(memoizer.apply(2D)).isEqualTo(4D);
+  }
+
+  @Test
+  void shouldMemoizeFunctionResult() {
+    AtomicInteger result = new AtomicInteger();
+
+    Function<Object, Integer> memoizer = Memoizers.of(d -> result.incrementAndGet());
+
+    assertThat(memoizer.apply(1)).isEqualTo(memoizer.apply(1));
+    assertThat(memoizer.apply(1)).isNotEqualTo(memoizer.apply(2));
+  }
+
+  @Test
+  void shouldMemoizeNullResult() {
+    NullFactory factory = new NullFactory();
+    Function<Object, String> memoizer = Memoizers.of(factory);
+
+    memoizer.apply(1);
+    memoizer.apply(1);
+
+    assertThat(factory.callsCount()).isEqualTo(1);
+    assertThat(memoizer.apply(1)).isNull();
+  }
+
+  @Test
+  void shouldMemoizeSupplier() {
+    Supplier<String> supplier = Memoizers.of(() -> "Pouet");
+
+    assertThat(supplier.get()).isEqualTo("Pouet");
+  }
+
+  private static class NullFactory implements Function<Object, String> {
+
+    private final AtomicInteger callsCount = new AtomicInteger();
+
+    public int callsCount() {
+      return callsCount.get();
+    }
+
+    @Override
+    public String apply(Object input) {
+      callsCount.incrementAndGet();
+
+      return null;
+    }
+  }
+}
diff --git a/readme.md b/readme.md
index 2da8b836..92959e14 100644
--- a/readme.md
+++ b/readme.md
@@ -86,6 +86,7 @@ Un kata de code est un petit exercice pensé pour s'entrainer jusqu'à maitriser
 
 -   [Concurrence en Java](/java-concurrence)
 -   [Try monade](/try-monade)
+-   [Memoizers](/java-memoizers)
 
 ### Bon Chance
 
-- 
GitLab