Skip to content

Commit 3c8b837

Browse files
committed
Merge pull request iluwatar#258 from amit2103/master
MonoState Pattern
2 parents 0a9879a + 271ce09 commit 3c8b837

File tree

10 files changed

+261
-2
lines changed

10 files changed

+261
-2
lines changed

monostate/etc/MonoState.ucls

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<class-diagram version="1.1.8" icons="true" always-add-relationships="false" generalizations="true" realizations="true"
3+
associations="true" dependencies="false" nesting-relationships="true">
4+
<class id="1" language="java" name="com.iluwatar.monostate.LoadBalancer" project="monostate"
5+
file="/monostate/src/main/java/com/iluwatar/monostate/LoadBalancer.java" binary="false" corner="BOTTOM_RIGHT">
6+
<position height="187" width="158" x="100" y="66"/>
7+
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
8+
sort-features="false" accessors="true" visibility="true">
9+
<attributes public="true" package="true" protected="true" private="true" static="true"/>
10+
<operations public="true" package="true" protected="true" private="true" static="true"/>
11+
</display>
12+
</class>
13+
<class id="2" language="java" name="com.iluwatar.monostate.Server" project="monostate"
14+
file="/monostate/src/main/java/com/iluwatar/monostate/Server.java" binary="false" corner="BOTTOM_RIGHT">
15+
<position height="-1" width="-1" x="176" y="372"/>
16+
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
17+
sort-features="false" accessors="true" visibility="true">
18+
<attributes public="true" package="true" protected="true" private="true" static="true"/>
19+
<operations public="true" package="true" protected="true" private="true" static="true"/>
20+
</display>
21+
</class>
22+
<class id="3" language="java" name="com.iluwatar.monostate.Request" project="monostate"
23+
file="/monostate/src/main/java/com/iluwatar/monostate/Request.java" binary="false" corner="BOTTOM_RIGHT">
24+
<position height="-1" width="-1" x="329" y="352"/>
25+
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
26+
sort-features="false" accessors="true" visibility="true">
27+
<attributes public="true" package="true" protected="true" private="true" static="true"/>
28+
<operations public="true" package="true" protected="true" private="true" static="true"/>
29+
</display>
30+
</class>
31+
<dependency id="4">
32+
<end type="SOURCE" refId="1"/>
33+
<end type="TARGET" refId="3"/>
34+
</dependency>
35+
<dependency id="5">
36+
<end type="SOURCE" refId="2"/>
37+
<end type="TARGET" refId="3"/>
38+
</dependency>
39+
<association id="6">
40+
<end type="SOURCE" refId="1" navigable="false">
41+
<attribute id="7" name="servers"/>
42+
<multiplicity id="8" minimum="0" maximum="2147483647"/>
43+
</end>
44+
<end type="TARGET" refId="2" navigable="true"/>
45+
<display labels="true" multiplicity="true"/>
46+
</association>
47+
<classifier-display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
48+
sort-features="false" accessors="true" visibility="true">
49+
<attributes public="true" package="true" protected="true" private="true" static="true"/>
50+
<operations public="true" package="true" protected="true" private="true" static="true"/>
51+
</classifier-display>
52+
<association-display labels="true" multiplicity="true"/>
53+
</class-diagram>

monostate/etc/monostate.png

16.1 KB
Loading

monostate/index.md

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
---
2+
layout: pattern
3+
title: MonoState
4+
folder: monostate
5+
permalink: /patterns/monostate/
6+
categories: Creational
7+
tags: Java
8+
---
9+
10+
**Intent:** Enforces a behaviour like sharing the same state amongst all instances.
11+
12+
![alt text](./etc/monostate.png "MonoState")
13+
14+
**Applicability:** Use the Monostate pattern when
15+
16+
* The same state must be shared across all instances of a class.
17+
* Typically this pattern might be used everywhere a Singleton might be used. Singleton usage however is not transparent, Monostate usage is.
18+
* 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.
19+
20+
**Typical Use Case:**
21+
22+
* the logging class
23+
* managing a connection to a database
24+
* file manager
25+
26+
**Real world examples:**
27+
28+
Yet to see this.

monostate/pom.xml

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<?xml version="1.0"?>
2+
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
4+
<modelVersion>4.0.0</modelVersion>
5+
<parent>
6+
<groupId>com.iluwatar</groupId>
7+
<artifactId>java-design-patterns</artifactId>
8+
<version>1.7.0</version>
9+
</parent>
10+
<artifactId>monostate</artifactId>
11+
<dependencies>
12+
<dependency>
13+
<groupId>junit</groupId>
14+
<artifactId>junit</artifactId>
15+
<scope>test</scope>
16+
</dependency>
17+
</dependencies>
18+
</project>
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package com.iluwatar.monostate;
2+
3+
4+
5+
/**
6+
*
7+
* The MonoState pattern ensures that all instances of the class will have the same state. This can
8+
* be used a direct replacement of the Singleton pattern.
9+
*
10+
* <p>
11+
* In the following example, The {@link LoadBalancer} class represents the app's logic. It contains
12+
* a series of Servers, which can handle requests of type {@link Request}. Two instances of
13+
* LoadBalacer are created. When a request is made to a server via the first LoadBalancer the state
14+
* change in the first load balancer affects the second. So if the first LoadBalancer selects the
15+
* Server 1, the second LoadBalancer on a new request will select the Second server. If a third
16+
* LoadBalancer is created and a new request is made to it, then it will select the third server as
17+
* the second load balancer has already selected the second server.
18+
* <p>
19+
* .
20+
*
21+
*/
22+
public class App {
23+
/**
24+
* Program entry point
25+
*
26+
* @param args command line args
27+
*/
28+
public static void main(String[] args) {
29+
LoadBalancer loadBalancer1 = new LoadBalancer();
30+
LoadBalancer loadBalancer2 = new LoadBalancer();
31+
loadBalancer1.serverequest(new Request("Hello"));
32+
loadBalancer2.serverequest(new Request("Hello World"));
33+
}
34+
35+
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
package com.iluwatar.monostate;
2+
3+
import java.util.ArrayList;
4+
import java.util.List;
5+
6+
/**
7+
* The LoadBalancer class. This implements the MonoState pattern. It holds a series of servers. Upon
8+
* receiving a new Request, it delegates the call to the servers in a Round Robin Fashion. Since all
9+
* instances of the class share the same state, all instances will delegate to the same server on
10+
* receiving a new Request.
11+
*
12+
*/
13+
14+
public class LoadBalancer {
15+
private static List<Server> servers = new ArrayList<>();
16+
private static int id = 0;
17+
private static int lastServedId = 0;
18+
19+
static {
20+
servers.add(new Server("localhost", 8081, ++id));
21+
servers.add(new Server("localhost", 8080, ++id));
22+
servers.add(new Server("localhost", 8082, ++id));
23+
servers.add(new Server("localhost", 8083, ++id));
24+
servers.add(new Server("localhost", 8084, ++id));
25+
}
26+
27+
public final void addServer(Server server) {
28+
synchronized (servers) {
29+
servers.add(server);
30+
}
31+
32+
}
33+
34+
public final int getNoOfServers() {
35+
return servers.size();
36+
}
37+
38+
public static int getLastServedId() {
39+
return lastServedId;
40+
}
41+
42+
public void serverequest(Request request) {
43+
if (lastServedId >= servers.size()) {
44+
lastServedId = 0;
45+
}
46+
Server server = servers.get(lastServedId++);
47+
server.serve(request);
48+
}
49+
50+
51+
52+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package com.iluwatar.monostate;
2+
3+
/**
4+
*
5+
* The Request class. A {@link Server} can handle an instance of a Request.
6+
*
7+
*/
8+
9+
public class Request {
10+
public final String value;
11+
12+
public Request(String value) {
13+
this.value = value;
14+
}
15+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package com.iluwatar.monostate;
2+
3+
/**
4+
*
5+
* The Server class. Each Server sits behind a LoadBalancer which delegates the call to the
6+
* servers in a simplistic Round Robin fashion.
7+
*
8+
*/
9+
public class Server {
10+
public final String host;
11+
public final int port;
12+
public final int id;
13+
14+
public Server(String host, int port, int id) {
15+
this.host = host;
16+
this.port = port;
17+
this.id = id;
18+
}
19+
20+
public String getHost() {
21+
return host;
22+
}
23+
24+
public int getPort() {
25+
return port;
26+
}
27+
28+
public final void serve(Request request) {
29+
System.out.println("Server ID " + id + " associated to host : " + getHost() + " and Port " + getPort() +" Processed request with value " + request.value);
30+
}
31+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package com.iluwatar.monostate;
2+
3+
import org.junit.Assert;
4+
import org.junit.Test;
5+
6+
public class AppTest {
7+
8+
@Test
9+
public void testSameStateAmonstAllInstances() {
10+
LoadBalancer balancer = new LoadBalancer();
11+
LoadBalancer balancer2 = new LoadBalancer();
12+
balancer.addServer(new Server("localhost", 8085, 6));
13+
// Both should have the same number of servers.
14+
Assert.assertTrue(balancer.getNoOfServers() == balancer2.getNoOfServers());
15+
// Both Should have the same LastServedId
16+
Assert.assertTrue(balancer.getLastServedId() == balancer2.getLastServedId());
17+
}
18+
19+
@Test
20+
public void testMain() {
21+
String[] args = {};
22+
App.main(args);
23+
Assert.assertTrue(LoadBalancer.getLastServedId() == 2);
24+
}
25+
26+
}

pom.xml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,9 +73,10 @@
7373
<module>front-controller</module>
7474
<module>repository</module>
7575
<module>async-method-invocation</module>
76-
<module>business-delegate</module>
77-
<module>half-sync-half-async</module>
76+
<module>monostate</module>
7877
<module>step-builder</module>
78+
<module>business-delegate</module>
79+
<module>half-sync-half-async</module>
7980
<module>layers</module>
8081
<module>message-channel</module>
8182
<module>fluentinterface</module>

0 commit comments

Comments
 (0)