context-object/README.md
Encapsulate the context (state and behaviors) relevant to the user or the request being processed to decouple Java application components from the complexities of the environment. This design pattern helps in managing the application's context efficiently.
Real-world example
Imagine a busy airport where multiple services need to access and share passenger information throughout their journey. Instead of each service requesting and passing passenger details separately, the airport uses a "Passenger Context Object." This context object holds all relevant passenger information, such as identity, flight details, and preferences. Various services like check-in, security, boarding, and customer service access this context object to get or update passenger data as needed. This approach ensures consistent and efficient information handling without tightly coupling the services, similar to how the Context Object design pattern works in software.
In plain words
Create an object to store and manage context data, and pass this context object wherever needed in the Java application, ensuring decoupled and cleaner code.
Core J2EE Patterns says
Use a Context Object to encapsulate state in a protocol-independent way to be shared throughout your application.
Sequence diagram
In a multi-layered Java application, different layers such as A, B, and C extract specific information from a shared context. Passing each piece of information individually is inefficient. The Context Object pattern efficiently stores and passes this information, improving the overall performance and maintainability of the Java application.
Define the data that the ServiceContext object contains.
@Getter
@Setter
public class ServiceContext {
String accountService;
String sessionService;
String searchService;
}
Create interface ServiceContextFactory to be used in parts of the application for context objects to be created.
public class ServiceContextFactory {
public static ServiceContext createContext() {
return new ServiceContext();
}
}
Instantiate the context object in the first layer. The adjoining layer calls the context in the current layer, which then further structures the object.
@Getter
public class LayerA {
private static ServiceContext context;
public LayerA() {
context = ServiceContextFactory.createContext();
}
public void addAccountInfo(String accountService) {
context.setACCOUNT_SERVICE(accountService);
}
}
@Getter
public class LayerB {
private static ServiceContext context;
public LayerB(LayerA layerA) {
this.context = layerA.getContext();
}
public void addSessionInfo(String sessionService) {
context.setSESSION_SERVICE(sessionService);
}
}
@Getter
public class LayerC {
public static ServiceContext context;
public LayerC(LayerB layerB) {
this.context = layerB.getContext();
}
public void addSearchInfo(String searchService) {
context.setSEARCH_SERVICE(searchService);
}
}
Here is the context object and layers in action.
@Slf4j
public class App {
private static final String SERVICE = "SERVICE";
public static void main(String[] args) {
//Initiate first layer and add service information into context
var layerA = new LayerA();
layerA.addAccountInfo(SERVICE);
logContext(layerA.getContext());
//Initiate second layer and preserving information retrieved in first layer through passing context object
var layerB = new LayerB(layerA);
layerB.addSessionInfo(SERVICE);
logContext(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);
logContext(layerC.getContext());
}
private static void logContext(ServiceContext context) {
LOGGER.info("Context = {}", context);
}
}
Program output:
08:15:32.134 [main] INFO com.iluwatar.context.object.App -- Context = com.iluwatar.context.object.ServiceContext@5577140b
08:15:32.136 [main] INFO com.iluwatar.context.object.App -- Context = com.iluwatar.context.object.ServiceContext@5577140b
08:15:32.137 [main] INFO com.iluwatar.context.object.App -- Context = com.iluwatar.context.object.ServiceContext@5577140b
Benefits:
Trade-offs: