Repository: iluwatar/java-design-patterns
Issue: Registry pattern
Labels: epic: pattern
type: feature

https://www.martinfowler.com/eaaCatalog/registry.html
https://stackoverflow.com/questions/16744424/whats-the-difference-between-singleton-and-registry-design-pattern
https://wiki.c2.com/?RegistryPattern

Issue comments:
Hi @iluwatar 
I would like to work on it.
Ok @viveksb007 
Hi @iluwatar,

I understood that Registry Pattern is similar to `Multiton` pattern, the only difference is no restrictions on numbers of objects.
In simple words, it stores <key, object> and there is no restriction on number of objects stored.

So I am thinking of creating a `CustomerRegistry` which will store customer objects (with `customerId` as key) and provide a global access to them. It requires adding/registering the customer object in the registry. For that I was thinking of creating a `CustomerFactory` which would create a `Customer` object and add it to registry. And anyone can get customer object by giving a `customerId` to registry. 

Wanted to clarify is it good adding a `Factory` to create customer object as I don't want any knowledge of registry inside the `Customer` object and don't want to manually add customer object to registry.

Any inputs would be helpful. 
Thanks!!

I've understood that typically Registry pattern is implemented with separated interface and implementation. Basically there's two methods in the registry object: One to add objects to the registry and another to query them. It's also good to demonstrate how testing is handled in the pattern. To your question whether Factory should be used or not, I would not create one. I think initializing the registry can be simply handled when the program starts i.e. calling something like `addCustomer` multiple times.

Pull request review comments:
Add an empty line after this
Done

Patch that resolved the issue:
diff --git a/pom.xml b/pom.xml
index e7fd2e4b3f87..86cefa13386a 100644
--- a/pom.xml
+++ b/pom.xml
@@ -195,6 +195,7 @@
         <module>strangler</module>
         <module>arrange-act-assert</module>
         <module>transaction-script</module>
+        <module>registry</module>
         <module>filterer</module>
         <module>factory</module>
         <module>separated-interface</module>
diff --git a/registry/README.md b/registry/README.md
new file mode 100644
index 000000000000..699c13318263
--- /dev/null
+++ b/registry/README.md
@@ -0,0 +1,86 @@
+---
+layout: pattern
+title: Registry
+folder: registry
+permalink: /patterns/registry/
+categories: Creational
+tags:
+ - Instantiation
+---
+
+## Intent
+Stores the objects of a single class and provide a global point of access to them. 
+Similar to Multiton pattern, only difference is that in a registry there is no restriction on the number of objects.
+
+## Explanation
+
+In Plain Words
+
+> Registry is a well-known object that other objects can use to find common objects and services.
+
+**Programmatic Example**
+Below is a `Customer` Class
+
+```java
+public class Customer {
+
+  private final String id;
+  private final String name;
+
+  public Customer(String id, String name) {
+    this.id = id;
+    this.name = name;
+  }
+
+  public String getId() {
+    return id;
+  }
+
+  public String getName() {
+    return name;
+  }
+
+}
+```
+
+This registry of the `Customer` objects is `CustomerRegistry`
+```java
+public final class CustomerRegistry {
+
+  private static final CustomerRegistry instance = new CustomerRegistry();
+
+  public static CustomerRegistry getInstance() {
+    return instance;
+  }
+
+  private final Map<String, Customer> customerMap;
+
+  private CustomerRegistry() {
+    customerMap = new ConcurrentHashMap<>();
+  }
+
+  public Customer addCustomer(Customer customer) {
+    return customerMap.put(customer.getId(), customer);
+  }
+
+  public Customer getCustomer(String id) {
+    return customerMap.get(id);
+  }
+
+}
+```
+
+## Class diagram
+![Registry](./etc/registry.png)
+
+## Applicability
+Use Registry pattern when 
+
+* client wants reference of some object, so client can lookup for that object in the object's registry.
+
+## Consequences
+Large number of bulky objects added to registry would result in a lot of memory consumption as objects in the registry are not garbage collected.
+
+## Credits
+* https://www.martinfowler.com/eaaCatalog/registry.html
+* https://wiki.c2.com/?RegistryPattern
diff --git a/registry/etc/registry.urm.puml b/registry/etc/registry.urm.puml
new file mode 100644
index 000000000000..77c6441fe688
--- /dev/null
+++ b/registry/etc/registry.urm.puml
@@ -0,0 +1,21 @@
+@startuml
+package com.iluwatar.registry {
+  class App {
+    - LOGGER : Logger {static}
+    + App()
+    + main(args : String[]) {static}
+  }
+  class Customer {
+    - id : String
+    - name : String
+    + getId() : String
+    + getName() : String
+    + toString() : String
+  }
+  class CustomerRegistry {
+    + addCustomer(customer : Customer)
+    + getCustomer(id : String)
+  }
+}
+Customer --> "-addCustomer" CustomerRegistry
+@enduml
diff --git a/registry/pom.xml b/registry/pom.xml
new file mode 100644
index 000000000000..852bcacfd73c
--- /dev/null
+++ b/registry/pom.xml
@@ -0,0 +1,46 @@
+<?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>
+    <groupId>com.iluwatar</groupId>
+    <artifactId>java-design-patterns</artifactId>
+    <version>1.24.0-SNAPSHOT</version>
+  </parent>
+  <artifactId>registry</artifactId>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.junit.jupiter</groupId>
+      <artifactId>junit-jupiter-engine</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>junit</groupId>
+      <artifactId>junit</artifactId>
+    </dependency>
+  </dependencies>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-assembly-plugin</artifactId>
+        <executions>
+          <execution>
+            <configuration>
+              <archive>
+                <manifest>
+                  <mainClass>com.iluwatar.registry.App</mainClass>
+                </manifest>
+              </archive>
+            </configuration>
+          </execution>
+        </executions>
+      </plugin>
+    </plugins>
+  </build>
+
+</project>
diff --git a/registry/src/main/java/com/iluwatar/registry/App.java b/registry/src/main/java/com/iluwatar/registry/App.java
new file mode 100644
index 000000000000..a593ce5cc15b
--- /dev/null
+++ b/registry/src/main/java/com/iluwatar/registry/App.java
@@ -0,0 +1,27 @@
+package com.iluwatar.registry;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class App {
+
+  private static final Logger LOGGER = LoggerFactory.getLogger(App.class);
+
+  /**
+   * Program entry point.
+   *
+   * @param args command line args
+   */
+  public static void main(String[] args) {
+    CustomerRegistry customerRegistry = CustomerRegistry.getInstance();
+    var john = new Customer("1", "John");
+    customerRegistry.addCustomer(john);
+
+    var julia = new Customer("2", "Julia");
+    customerRegistry.addCustomer(julia);
+
+    LOGGER.info("John {}", customerRegistry.getCustomer("1"));
+    LOGGER.info("Julia {}", customerRegistry.getCustomer("2"));
+  }
+
+}
diff --git a/registry/src/main/java/com/iluwatar/registry/Customer.java b/registry/src/main/java/com/iluwatar/registry/Customer.java
new file mode 100644
index 000000000000..354999fdf2ce
--- /dev/null
+++ b/registry/src/main/java/com/iluwatar/registry/Customer.java
@@ -0,0 +1,28 @@
+package com.iluwatar.registry;
+
+public class Customer {
+
+  private final String id;
+  private final String name;
+
+  public Customer(String id, String name) {
+    this.id = id;
+    this.name = name;
+  }
+
+  public String getId() {
+    return id;
+  }
+
+  public String getName() {
+    return name;
+  }
+
+  @Override
+  public String toString() {
+    return "Customer{"
+        + "id='" + id + '\''
+        + ", name='" + name + '\''
+        + '}';
+  }
+}
diff --git a/registry/src/main/java/com/iluwatar/registry/CustomerRegistry.java b/registry/src/main/java/com/iluwatar/registry/CustomerRegistry.java
new file mode 100644
index 000000000000..406d616c678c
--- /dev/null
+++ b/registry/src/main/java/com/iluwatar/registry/CustomerRegistry.java
@@ -0,0 +1,28 @@
+package com.iluwatar.registry;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+public final class CustomerRegistry {
+
+  private static final CustomerRegistry instance = new CustomerRegistry();
+
+  public static CustomerRegistry getInstance() {
+    return instance;
+  }
+
+  private final Map<String, Customer> customerMap;
+
+  private CustomerRegistry() {
+    customerMap = new ConcurrentHashMap<>();
+  }
+
+  public Customer addCustomer(Customer customer) {
+    return customerMap.put(customer.getId(), customer);
+  }
+
+  public Customer getCustomer(String id) {
+    return customerMap.get(id);
+  }
+
+}


Test patch:
diff --git a/registry/src/test/java/com/iluwatar/registry/CustomerRegistryTest.java b/registry/src/test/java/com/iluwatar/registry/CustomerRegistryTest.java
new file mode 100644
index 000000000000..2919390b9270
--- /dev/null
+++ b/registry/src/test/java/com/iluwatar/registry/CustomerRegistryTest.java
@@ -0,0 +1,44 @@
+package com.iluwatar.registry;
+
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+
+public class CustomerRegistryTest {
+
+  private static CustomerRegistry customerRegistry;
+
+  @BeforeAll
+  public static void setUp() {
+    customerRegistry = CustomerRegistry.getInstance();
+  }
+
+  @Test
+  public void shouldBeAbleToAddAndQueryCustomerObjectFromRegistry() {
+    Customer john = new Customer("1", "john");
+    Customer julia = new Customer("2", "julia");
+
+    customerRegistry.addCustomer(john);
+    customerRegistry.addCustomer(julia);
+
+    Customer customerWithId1 = customerRegistry.getCustomer("1");
+    assertNotNull(customerWithId1);
+    assertEquals("1", customerWithId1.getId());
+    assertEquals("john", customerWithId1.getName());
+
+    Customer customerWithId2 = customerRegistry.getCustomer("2");
+    assertNotNull(customerWithId2);
+    assertEquals("2", customerWithId2.getId());
+    assertEquals("julia", customerWithId2.getName());
+  }
+
+  @Test
+  public void shouldReturnNullWhenQueriedCustomerIsNotInRegistry() {
+    Customer customerWithId5 = customerRegistry.getCustomer("5");
+    assertNull(customerWithId5);
+  }
+
+}