diff --git a/monostate/etc/MonoState.ucls b/monostate/etc/MonoState.ucls new file mode 100644 index 000000000..76b48ad0f --- /dev/null +++ b/monostate/etc/MonoState.ucls @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/monostate/etc/monostate.png b/monostate/etc/monostate.png new file mode 100644 index 000000000..273fa9545 Binary files /dev/null and b/monostate/etc/monostate.png differ diff --git a/monostate/index.md b/monostate/index.md new file mode 100644 index 000000000..2b88f131e --- /dev/null +++ b/monostate/index.md @@ -0,0 +1,28 @@ +--- +layout: pattern +title: MonoState +folder: monostate +permalink: /patterns/monostate/ +categories: Creational +tags: Java +--- + +**Intent:** Enforces a behaviour like sharing the same state amongst all instances. + +![alt text](./etc/monostate.png "MonoState") + +**Applicability:** Use the Monostate pattern when + +* The same state must be shared across all instances of a class. +* Typically this pattern might be used everywhere a Singleton might be used. Singleton usage however is not transparent, Monostate usage is. +* Monostate has one major advantage over singleton. The subclasses might decorate the shared state as they wish and hence can provide dynamically different behaviour than the base class. + +**Typical Use Case:** + +* the logging class +* managing a connection to a database +* file manager + +**Real world examples:** + +Yet to see this. diff --git a/monostate/pom.xml b/monostate/pom.xml new file mode 100644 index 000000000..480a4967c --- /dev/null +++ b/monostate/pom.xml @@ -0,0 +1,18 @@ + + + 4.0.0 + + com.iluwatar + java-design-patterns + 1.7.0 + + monostate + + + junit + junit + test + + + diff --git a/monostate/src/main/java/com/iluwatar/monostate/App.java b/monostate/src/main/java/com/iluwatar/monostate/App.java new file mode 100644 index 000000000..0daad5b67 --- /dev/null +++ b/monostate/src/main/java/com/iluwatar/monostate/App.java @@ -0,0 +1,35 @@ +package com.iluwatar.monostate; + + + +/** + * + * The MonoState pattern ensures that all instances of the class will have the same state. This can + * be used a direct replacement of the Singleton pattern. + * + *

+ * In the following example, The {@link LoadBalancer} class represents the app's logic. It contains + * a series of Servers, which can handle requests of type {@link Request}. Two instances of + * LoadBalacer are created. When a request is made to a server via the first LoadBalancer the state + * change in the first load balancer affects the second. So if the first LoadBalancer selects the + * Server 1, the second LoadBalancer on a new request will select the Second server. If a third + * LoadBalancer is created and a new request is made to it, then it will select the third server as + * the second load balancer has already selected the second server. + *

+ * . + * + */ +public class App { + /** + * Program entry point + * + * @param args command line args + */ + public static void main(String[] args) { + LoadBalancer loadBalancer1 = new LoadBalancer(); + LoadBalancer loadBalancer2 = new LoadBalancer(); + loadBalancer1.serverequest(new Request("Hello")); + loadBalancer2.serverequest(new Request("Hello World")); + } + +} diff --git a/monostate/src/main/java/com/iluwatar/monostate/LoadBalancer.java b/monostate/src/main/java/com/iluwatar/monostate/LoadBalancer.java new file mode 100644 index 000000000..7bc0043e8 --- /dev/null +++ b/monostate/src/main/java/com/iluwatar/monostate/LoadBalancer.java @@ -0,0 +1,52 @@ +package com.iluwatar.monostate; + +import java.util.ArrayList; +import java.util.List; + +/** + * The LoadBalancer class. This implements the MonoState pattern. It holds a series of servers. Upon + * receiving a new Request, it delegates the call to the servers in a Round Robin Fashion. Since all + * instances of the class share the same state, all instances will delegate to the same server on + * receiving a new Request. + * + */ + +public class LoadBalancer { + private static List servers = new ArrayList<>(); + private static int id = 0; + private static int lastServedId = 0; + + static { + servers.add(new Server("localhost", 8081, ++id)); + servers.add(new Server("localhost", 8080, ++id)); + servers.add(new Server("localhost", 8082, ++id)); + servers.add(new Server("localhost", 8083, ++id)); + servers.add(new Server("localhost", 8084, ++id)); + } + + public final void addServer(Server server) { + synchronized (servers) { + servers.add(server); + } + + } + + public final int getNoOfServers() { + return servers.size(); + } + + public static int getLastServedId() { + return lastServedId; + } + + public void serverequest(Request request) { + if (lastServedId >= servers.size()) { + lastServedId = 0; + } + Server server = servers.get(lastServedId++); + server.serve(request); + } + + + +} diff --git a/monostate/src/main/java/com/iluwatar/monostate/Request.java b/monostate/src/main/java/com/iluwatar/monostate/Request.java new file mode 100644 index 000000000..ee1f31d85 --- /dev/null +++ b/monostate/src/main/java/com/iluwatar/monostate/Request.java @@ -0,0 +1,15 @@ +package com.iluwatar.monostate; + +/** + * + * The Request class. A {@link Server} can handle an instance of a Request. + * + */ + +public class Request { + public final String value; + + public Request(String value) { + this.value = value; + } +} diff --git a/monostate/src/main/java/com/iluwatar/monostate/Server.java b/monostate/src/main/java/com/iluwatar/monostate/Server.java new file mode 100644 index 000000000..ce4e0222d --- /dev/null +++ b/monostate/src/main/java/com/iluwatar/monostate/Server.java @@ -0,0 +1,31 @@ +package com.iluwatar.monostate; + +/** + * + * The Server class. Each Server sits behind a LoadBalancer which delegates the call to the + * servers in a simplistic Round Robin fashion. + * + */ +public class Server { + public final String host; + public final int port; + public final int id; + + public Server(String host, int port, int id) { + this.host = host; + this.port = port; + this.id = id; + } + + public String getHost() { + return host; + } + + public int getPort() { + return port; + } + + public final void serve(Request request) { + System.out.println("Server ID " + id + " associated to host : " + getHost() + " and Port " + getPort() +" Processed request with value " + request.value); + } +} diff --git a/monostate/src/test/java/com/iluwatar/monostate/AppTest.java b/monostate/src/test/java/com/iluwatar/monostate/AppTest.java new file mode 100644 index 000000000..c5f1f7e92 --- /dev/null +++ b/monostate/src/test/java/com/iluwatar/monostate/AppTest.java @@ -0,0 +1,26 @@ +package com.iluwatar.monostate; + +import org.junit.Assert; +import org.junit.Test; + +public class AppTest { + + @Test + public void testSameStateAmonstAllInstances() { + LoadBalancer balancer = new LoadBalancer(); + LoadBalancer balancer2 = new LoadBalancer(); + balancer.addServer(new Server("localhost", 8085, 6)); + // Both should have the same number of servers. + Assert.assertTrue(balancer.getNoOfServers() == balancer2.getNoOfServers()); + // Both Should have the same LastServedId + Assert.assertTrue(balancer.getLastServedId() == balancer2.getLastServedId()); + } + + @Test + public void testMain() { + String[] args = {}; + App.main(args); + Assert.assertTrue(LoadBalancer.getLastServedId() == 2); + } + +} diff --git a/pom.xml b/pom.xml index 3064f3c0d..262e86856 100644 --- a/pom.xml +++ b/pom.xml @@ -73,9 +73,10 @@ front-controller repository async-method-invocation - business-delegate - half-sync-half-async + monostate step-builder + business-delegate + half-sync-half-async layers message-channel fluentinterface