feature: Add context object pattern (#2304)

* Add context object pattern and corresponding tests

* Update context pattern

* Add README, class diagram, puml file.
Add toString method in ServiceContext.java.
Add tests for coverage.

* Improvements:
Remove plugin in pom file
Add comments in app.java
Change local variable keyword to var in app.java
Use lombok for getters, setters and tostring
Change method signature in context object

* Refreshing environment-1

* Reconfigure pom file.

Co-authored-by: Alvis Chan <u7287079@edu.anu.au>
Co-authored-by: u7287079 <u7287079@anu.edu.au>
This commit is contained in:
Chak-C
2022-12-29 11:17:28 +02:00
committed by GitHub
co-authored by Alvis Chan u7287079
parent 695e12379d
commit e223459db8
12 changed files with 498 additions and 0 deletions
@@ -0,0 +1,41 @@
package com.iluwatar.context.object;
import lombok.extern.slf4j.Slf4j;
/**
* In the context object pattern, information and data from underlying protocol-specific classes/systems is decoupled
* and stored into a protocol-independent object in an organised format. The pattern ensures the data contained within
* the context object can be shared and further structured between different layers of a software system.
*
* <p> In this example we show how a context object {@link ServiceContext} can be initiated, edited and passed/retrieved
* in different layers of the program ({@link LayerA}, {@link LayerB}, {@link LayerC}) through use of static methods. </p>
*/
@Slf4j
public class App {
private static final String SERVICE = "SERVICE";
/**
* Program entry point.
* @param args command line args
*/
public static void main(String[] args) {
//Initiate first layer and add service information into context
var layerA = new LayerA();
layerA.addAccountInfo(SERVICE);
LOGGER.info("Context = {}",layerA.getContext());
//Initiate second layer and preserving information retrieved in first layer through passing context object
var layerB = new LayerB(layerA);
layerB.addSessionInfo(SERVICE);
LOGGER.info("Context = {}",layerB.getContext());
//Initiate third layer and preserving information retrieved in first and second layer through passing context object
var layerC = new LayerC(layerB);
layerC.addSearchInfo(SERVICE);
LOGGER.info("Context = {}",layerC.getContext());
}
}
@@ -0,0 +1,17 @@
package com.iluwatar.context.object;
import lombok.Getter;
@Getter
public class LayerA {
private ServiceContext context;
public LayerA() {
context = ServiceContextFactory.createContext();
}
public void addAccountInfo(String accountService) {
context.setAccountService(accountService);
}
}
@@ -0,0 +1,17 @@
package com.iluwatar.context.object;
import lombok.Getter;
@Getter
public class LayerB {
private ServiceContext context;
public LayerB(LayerA layerA) {
this.context = layerA.getContext();
}
public void addSessionInfo(String sessionService) {
context.setSessionService(sessionService);
}
}
@@ -0,0 +1,17 @@
package com.iluwatar.context.object;
import lombok.Getter;
@Getter
public class LayerC {
public ServiceContext context;
public LayerC(LayerB layerB) {
this.context = layerB.getContext();
}
public void addSearchInfo(String searchService) {
context.setSearchService(searchService);
}
}
@@ -0,0 +1,16 @@
package com.iluwatar.context.object;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
/**
* Where context objects are defined.
*/
@ToString
@Getter
@Setter
public class ServiceContext {
String AccountService, SessionService, SearchService;
}
@@ -0,0 +1,11 @@
package com.iluwatar.context.object;
/**
* An interface to create context objects passed through layers.
*/
public class ServiceContextFactory {
public static ServiceContext createContext() {
return new ServiceContext();
}
}
@@ -0,0 +1,17 @@
package com.iluwatar.contect.object;
import com.iluwatar.context.object.App;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
public class AppTest {
/**
* Test example app runs without error.
*/
@Test
void shouldExecuteWithoutException() {
assertDoesNotThrow(() -> App.main(new String[]{}));
}
}
@@ -0,0 +1,66 @@
package com.iluwatar.contect.object;
import com.iluwatar.context.object.LayerA;
import com.iluwatar.context.object.LayerB;
import com.iluwatar.context.object.LayerC;
import com.iluwatar.context.object.ServiceContext;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertSame;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNull;
/**
* Date: 10/24/2022 - 3:18
*
* @author Chak Chan
*/
public class ServiceContextTest {
private static final String SERVICE = "SERVICE";
private LayerA layerA;
@BeforeEach
void initiateLayerA() {
this.layerA = new LayerA();
}
@Test
void testSameContextPassedBetweenLayers() {
ServiceContext context1 = layerA.getContext();
var layerB = new LayerB(layerA);
ServiceContext context2 = layerB.getContext();
var layerC = new LayerC(layerB);
ServiceContext context3 = layerC.getContext();
assertSame(context1, context2);
assertSame(context2, context3);
assertSame(context3, context1);
}
@Test
void testScopedDataPassedBetweenLayers() {
layerA.addAccountInfo(SERVICE);
var layerB = new LayerB(layerA);
var layerC = new LayerC(layerB);
layerC.addSearchInfo(SERVICE);
ServiceContext context = layerC.getContext();
assertEquals(SERVICE,context.getAccountService());
assertNull(context.getSessionService());
assertEquals(SERVICE,context.getSearchService());
}
@Test
void testToString() {
assertEquals(layerA.getContext().toString(),"ServiceContext(AccountService=null, SessionService=null, SearchService=null)");
layerA.addAccountInfo(SERVICE);
assertEquals(layerA.getContext().toString(), "ServiceContext(AccountService=SERVICE, SessionService=null, SearchService=null)");
var layerB = new LayerB(layerA);
layerB.addSessionInfo(SERVICE);
var layerC = new LayerC(layerB);
assertEquals(layerC.getContext().toString(), "ServiceContext(AccountService=SERVICE, SessionService=SERVICE, SearchService=null)");
}
}