Overview
This is the fourth and final article in a short series dedicated to Design Patterns in Java, and a direct continuation from the previous article - Behavioral Design Patterns in Java.
J2EE Patterns
J2EE Patterns are concerned about providing solutions regarding Java EE. These patterns are widely accepted by other frameworks and projects. Like, for an example: Spring.
The J2EE Patterns that are covered in this article are:
- MVC Pattern
- Business Delegate Pattern
- Composite Entity Pattern
- Data Access Object Pattern
- Front Controller Pattern
- Intercepting Filter Pattern
- Service Locator Pattern
- Transfer Object Pattern
MVC Pattern
This is one of the most notorious and most-used patterns from this category. It revolves around the idea of Model-View-Controller, which is where the abbreviation comes from.
Models are basically objects, or POJO's to be exact, used as blueprints/models for all of the objects that will be used in the application.
Views represent the presentational aspect of the data and information located in the models.
Controllers controls both of these. They serve as a connection between the two. Controllers both instantiate, update and delete models, populate them with information, and then send the data to the views to present to the end-user.
Implementation
That being said, let's start with the first of the three components of this pattern - the model:
public class Employee {
private int employeeId;
private String name;
public int getEmployeeId() {
return employeeId;
}
public void setEmployeeId(int id) {
this.employeeId = id;
}
public String getName() {
return name;
}
public void setEmployeeName(String name) {
this.name = name;
}
}
We need a way to present the data from the model, so we define a view for that very purpose:
public class EmployeeView {
public void printEmployeeInformation(String employeeName, int employeeId) {
System.out.println("Employee information: ");
System.out.println("ID: " + employeeId);
System.out.println("Name: " + employeeName);
}
}
The view is responsible for formatting the information in a user-friendly way.
Once that is out of the way, let's define the controller. This controller will utilize both the model and the view to instantiate the model, populate it with some data, and then push it to the view for the client to see:
public class EmployeeController {
private Employee employee;
private EmployeeView employeeView;
public EmployeeController(Employee employee, EmployeeView employeeView) {
this.employee = employee;
this.employeeView = employeeView;
}
public String getEmployeeName() {
return employee.getName();
}
public void setEmployeeName(String name) {
employee.setEmployeeName(name);
}
public int getEmployeeId() {
return employee.getEmployeeId();
}
public void setEmployeeId(int id) {
employee.setEmployeeId(id);
}
public void updateView() {
employeeView.printEmployeeInformation(employee.getName(), employee.getEmployeeId());
}
}
With all three components of this pattern complete, we can wrap this example up.
To illustrate the point of this pattern:
public class Main {
public static void main(String[] args) {
Employee employee = getEmployeeFromDatabase();
EmployeeView view = new EmployeeView();
EmployeeController controller = new EmployeeController(employee, view);
controller.updateView();
controller.setEmployeeId(5);
controller.updateView();
}
// simulating a database
public static Employee getEmployeeFromDatabase() {
Employee employee = new Employee();
employee.setEmployeeName("David");
employee.setEmployeeId(1);
return employee;
}
}
Running this piece of code will yield:
Employee information:
ID: 1
Name: David
Employee information:
ID: 5
Name: David
Business Delegate Pattern
The Business Delegate pattern is used to decouple the presentation layer from the business layer to minimize the number of requests between the client (presentation) and the business tiers.
Implementation
Let's start off by defining an interface for our business services:
public interface BusinessService {
public void process();
}
Afterwards, let's define two concrete classes implementing this interface:
public class EJBService implements BusinessService {
@Override
public void process() {
System.out.println("Processing using the EJB Service.");
}
}
public class JMSService implements BusinessService {
@Override
public void process() {
System.out.println("Processing using the JSM Service.");
}
}
Let's define a lookup service. The lookup service object should provide the relative business implementations and business object access to the business delegate logic:
public class BusinessLookUp {
public BusinessService getBusinessService(String type) {
if (type.equalsIgnoreCase("ejb")) {
return new EJBService();
} else if (type.equalsIgnoreCase("JMS")) {
return new JMSService();
} else {
return null;
}
}
}
Now, we can define our business delegate:
public class BusinessDelegate {
private BusinessLookUp lookupService = new BusinessLookUp();
private BusinessService businessService;
private String type;
public void setServiceType(String type) {
this.type = type;
}
public void process() {
businessService = lookupService.getBusinessService(type);
businessService.process();
}
}
It acts as an access point to the business services for the Client
to use:
public class Client {
BusinessDelegate businessDelegate;
public Client(BusinessDelegate businessDelegate) {
this.businessDelegate = businessDelegate;
}
public void process() {
businessDelegate.process();
}
}
And now to illustrate the point of this pattern:
public class Main {
public static void main(String[] args) {
BusinessDelegate businessDelegate = new BusinessDelegate();
businessDelegate.setServiceType("EJB");
Client client = new Client(businessDelegate);
client.process();
businessDelegate.setServiceType("JMS");
client.process();
}
}
Running this piece of code will yield:
Processing using the EJB Service.
Processing using the JSM Service.
Composite Entity Pattern
The Composite Entity pattern represents a graph of objects, which when updated, triggers an update for all the dependent entities in the graph.
It is mainly employed in Enterprise JavaBeans (EJB) which isn't a very popular API as it has been replaced by other frameworks and tools like the Spring Framework and its numerous tools.
Implementation
Let's define two classes which feature data that would need to update another class:
public class Employee {
private String name;
private String jobSuccess;
public void setJobSuccess(String jobSuccess) {
this.jobSuccess = jobSuccess;
}
public String getJobSuccess() {
return jobSuccess;
}
}
public class Manager {
private String name;
private String satisfaction;
public void setSatisfaction(String satisfaction) {
this.satisfaction = satisfaction;
}
public String getSatisfaction() {
return satisfaction;
}
}
If the Employee
does well, the Manager
is satisfied and vice versa.
Since the point of this pattern is to not allow the beans to act as "fine-grained" objects alone, we're introduced with a Coarse-Grained Object. This object manages its own relationships to other objects:
public class CoarseGrainedObject {
Employee employee = new Employee();
Manager manager = new Manager();
public void setData(String jobSuccess, String satisfaction) {
employee.setJobSuccess(jobSuccess);
manager.setSatisfaction(satisfaction);
}
public String[] getData() {
return new String[] {"Employee : " + employee.getJobSuccess(),"Manager: " +
manager.getSatisfaction()};
}
}
Afterwards, we need to define a CompositeEntity
class. This class is itself a coarse-grained object and can reference another:
public class CompositeEntity {
private CoarseGrainedObject cgo = new CoarseGrainedObject();
public void setData(String jobSuccess, String satisfaction) {
cgo.setData(jobSuccess, satisfaction);
}
public String[] getData() {
return cgo.getData();
}
}
With that in place, we just need a Client
to use the CompositeEntity
:
public class Client {
private CompositeEntity compositeEntity = new CompositeEntity();
public void print() {
for (int i = 0; i < compositeEntity.getData().length; i++) {
System.out.println(compositeEntity.getData()[i]);
}
}
public void setData(String jobSuccess, String satisfaction) {
compositeEntity.setData(jobSuccess, satisfaction);
}
}
And to illustrate the point of this pattern:
public class Main {
public static void main(String[] args) {
Client client = new Client();
client.setData("Successful", "Satisfied");
client.print();
client.setData("Failed", "Unsatisfied");
client.print();
}
}
Running this piece of code will yield:
Employee : Successful
Manager: Satisfied
Employee : Failed
Manager: Unsatisfied
Data Access Object Pattern
The Data Access Object pattern, most often shortened to DAO is a pattern in which objects are dedicated to the communication with the Data Layer.
These objects often instantiate "SessionFactories" for this purpose and handle all of the logic behind communicating with the database.
The standard practice is to create a DAO interface, followed by a concrete class implementing the interface and all methods defined in it.
Implementation
Following the standard practice, let's define our DAO interface:
public interface EmployeeDAO {
public List<Employee> getAllEmployees();
public Employee getEmployeeById(int id);
public void addEmployee(Employee e);
public void updateEmployee(Employee e);
public void deleteEmployee(Employee e);
}
And our concrete implementation class along with it:
public class EmployeeDAOImpl implements EmployeeDAO {
List<Employee> employeeList;
public EmployeeDAOImpl() {
employeeList = new ArrayList<Employee>();
Employee david = new Employee(5, "David");
Employee scott = new Employee(7, "Scott");
Employee jessica = new Employee(12, "Jessica");
Employee rebecca = new Employee(16, "Rebecca");
employeeList.add(david);
employeeList.add(scott);
employeeList.add(jessica);
employeeList.add(rebecca);
}
@Override
public List<Employee> getAllEmployees() {
return employeeList;
}
@Override
public Employee getEmployeeById(int id) {
return employeeList.get(id);
}
@Override
public void addEmployee(Employee e) {
employeeList.add(e);
System.out.println("Successfully added " + e.getName());
}
@Override
public void updateEmployee(Employee e) {
employeeList.get(e.getEmployeeId()).setEmployeeName(e.getName());
System.out.println("Successfully update name of employee with id: " + e.getEmployeeId());
}
@Override
public void deleteEmployee(Employee e) {
employeeList.remove(e.getEmployeeId());
System.out.println("Successfully removed employee: " + e.getName() + "with the ID: " + e.getEmployeeId());
}
}
We will be using these two classes to add, retrieve, update, or delete users from our database:
public class Employee {
private int employeeId;
private String name;
public Employee(int id, String name) {
this.employeeId = id;
this.name = name;
}
public int getEmployeeId() {
return employeeId;
}
public void setEmployeeId(int id) {
this.employeeId = id;
}
public String getName() {
return name;
}
public void setEmployeeName(String name) {
this.name = name;
}
}
And to illustrate the point of this pattern:
public class Main {
public static void main(String[] args) {
EmployeeDAO employeeDao = new EmployeeDAOImpl();
for(Employee employee : employeeDao.getAllEmployees()) {
System.out.println("Employee info: |Name: " + employee.getName() + ", ID: " + employee.getEmployeeId() + "|");
}
}
}
Check out our hands-on, practical guide to learning Git, with best-practices, industry-accepted standards, and included cheat sheet. Stop Googling Git commands and actually learn it!
Running this piece of code will yield:
Employee info: |Name: David, ID: 5|
Employee info: |Name: Scott, ID: 7|
Employee info: |Name: Jessica, ID: 12|
Employee info: |Name: Rebecca, ID: 16|
Front Controller Pattern
Upon sending a request, the Front Controller is the first controller it reaches. Based on the request, it decides which controller is the most adequate to handle it, after which it passes the request to the chosen controller.
The Front Controller is most often used in Web Applications in the form of a Dispatcher Servlet.
Implementation
For this implementation, we'll be defining two simple views, a FrontController
and a Dispatcher
:
public class MainView {
public void showView() {
System.out.println("Showing main view.");
}
}
public class EmployeeView {
public void showView() {
System.out.println("Showing Employee view.");
}
}
A request for either of these two can come up at any point. We use the Dispatcher
to deal with the request, pointing to the correct view, after the FrontController
processed the request initially:
public class Dispatcher {
private MainView mainView;
private EmployeeView employeeView;
public Dispatcher() {
mainView = new MainView();
employeeView = new EmployeeView();
}
public void dispatch(String request) {
if(request.equalsIgnoreCase("EMPLOYEE")) {
employeeView.showView();
} else {
mainView.showView();
}
}
}
public class FrontController {
private Dispatcher dispatcher;
public FrontController() {
dispatcher = new Dispatcher();
}
private boolean isAuthenticUser() {
System.out.println("User has successfully authenticated.");
return true;
}
private void trackRequest(String request) {
System.out.println("Request: " + request);
}
public void dispatchRequest(String request) {
trackRequest(request);
if(isAuthenticUser()) {
dispatcher.dispatch(request);
}
}
}
And to illustrate the point of the pattern:
public class Main {
public static void main(String[] args) {
FrontController frontController = new FrontController();
frontController.dispatchRequest("MAIN");
frontController.dispatchRequest("EMPLOYEE");
}
}
Running this piece of code will yield:
Request: MAIN
User has successfully authenticated.
Showing main view.
Request: EMPLOYEE
User has successfully authenticated.
Showing Employee view.
Intercepting Filter Pattern
Filters are used before the request is even passed to the adequate controllers for processing. These filters can exist in the form of a Filter Chain and include multiple filters, or simply exist as one Filter.
Nevertheless, they run checks on authorization, authentication, supported browsers, whether the request path violates any constraints and restrictions etc.
Implementation
We'll make a simple filter chain with a couple of filters to intercept the request after reaching the target.
Let's start off with defining an interface for the Filter
itself:
public interface Filter {
public void execute(String request);
}
And a couple of concrete implementations:
public class AuthenticationFilter implements Filter {
@Override
public void execute(String request) {
System.out.println("Authentication request: " + request);
}
}
public class DebuggingFilter implements Filter {
@Override
public void execute(String request) {
System.out.println("Logging request: " + request);
}
}
And finally, the Target
of the request:
public class Target {
public void execute(String request) {
System.out.println("Executing request: " + request);
}
}
By defining a FilterChain
, we can add multiple filters to intercept a request. Let's define one for our two filters:
public class FilterChain {
private List<Filter> filters = new ArrayList<>();
private Target target;
public void addFilter(Filter filter) {
filters.add(filter);
}
public void execute(String request) {
for (Filter filter : filters) {
filter.execute(request);
}
target.execute(request);
}
public void setTarget(Target target) {
this.target = target;
}
}
We now need a manager class to help manage this FilterChain
:
public class FilterManager {
FilterChain filterChain;
public FilterManager(Target target) {
filterChain = new FilterChain();
filterChain.setTarget(target);
}
public void addFilter(Filter filter) {
filterChain.addFilter(filter);
}
public void filterRequest(String request) {
filterChain.execute(request);
}
}
And finally, the Client
will use the FilterManager
to send a request to the application:
public class Client {
FilterManager filterManager;
public void setFilterManager(FilterManager filterManager) {
this.filterManager = filterManager;
}
public void sendRequest(String request) {
filterManager.filterRequest(request);
}
}
Now to illustrate the point of this pattern:
public class Main {
public static void main(String[] args) {
FilterManager filterManager = new FilterManager(new Target());
filterManager.addFilter(new AuthenticationFilter());
filterManager.addFilter(new DebuggingFilter());
Client client = new Client();
client.setFilterManager(filterManager);
client.sendRequest("Index");
}
}
Running this piece of code will yield:
Authentication request: Index
Logging request: Index
Executing request: Index
The request has been put through both filters from the FilterChain
, before being forwarded to the Target
.
Service Locator Pattern
A pattern often seen in Web Applications, the Service Locator pattern is used to decouple the Service Consumers and the concrete classes like DAO implementations.
The pattern looks for the adequate service, saves it in cache storage to reduce the number of requests and therefore the strain on the server and provides the application with their instances.
Implementation
Let's start this implementation by defining a common Service
interface:
public interface Service {
public String getServiceName();
public void execute();
}
A couple of concrete classes will implement this interface:
public class EmployeeService implements Service {
@Override
public String getServiceName() {
return "Employee Service";
}
@Override
public void execute() {
System.out.println("Executing Employee Service...");
}
}
public class CustomerService implements Service {
@Override
public String getServiceName() {
return "Customer Service";
}
@Override
public void execute() {
System.out.println("Executing Customer Service...");
}
}
According to the pattern, when looking up for these services, we should cache them to reduce server strain:
public class Cache {
private List<Service> services;
public Cache() {
services = new ArrayList<Service>();
}
public Service getService(String serviceName) {
for(Service service : services) {
if(service.getServiceName().equalsIgnoreCase(serviceName)) {
System.out.println("Returning cached " + serviceName);
return service;
}
}
return null;
}
public void addService(Service newService) {
boolean exists = false;
for(Service service : services){
if(service.getServiceName().equalsIgnoreCase(newService.getServiceName())) {
exists = true;
}
}
if(!exists) {
services.add(newService);
}
}
}
We also need a class to look for, and instantiate our services:
public class InitialContext {
public Object lookup(String jndiName) {
if(jndiName.equalsIgnoreCase("EmployeeService")) {
System.out.println("Looking up and initializing Employee Service...");
return new EmployeeService();
} else if(jndiName.equalsIgnoreCase("CustomerService")) {
System.out.println("Looking up and initializing Customer Service...");
return new CustomerService();
}
return null;
}
}
And finally, we can define a Locator
class to expose to the client, that uses the InitialContext
class to look for services, and the Cache
class to cache them for further use.
public class Locator {
private static Cache cache;
static {
cache = new Cache();
}
public static Service getService(String jndiName) {
Service service = cache.getService(jndiName);
if(service != null) {
return service;
}
InitialContext context = new InitialContext();
Service service1 = (Service)context.lookup(jndiName);
cache.addService(service1);
return service1;
}
}
And to illustrate the point of this pattern:
public class Main {
public static void main(String[] args) {
Service service = Locator.getService("EmployeeService");
service.execute();
service = Locator.getService("CustomerService");
service.execute();
}
}
Running this piece of code will yield:
Looking up and initializing Employee Service...
Executing Employee Service...
Looking up and initializing Customer Service...
Executing Customer Service...
Transfer Object Pattern
This pattern is used to transfer objects with lots of fields and parameters in one go. The Transfer Object pattern employs new objects, used only for transfer purposes, usually passed to the DAO.
These objects are serializable POJOs. They have fields, their respective getters and setters, and no other logic.
Implementation
An object may look like this:
public class EmployeeVO {
private int employeeId;
private String name;
public EmployeeVO(int employeeId, String name) {
this.employeeId = employeeId;
this.name = name;
}
public int getEmployeeId() {
return employeeId;
}
public void setEmployeeId(int id) {
this.employeeId = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Please note that the object contains only a few fields for brevity.
An example of a new object, that's employed for transfer purposes only:
public class EmployeeBO {
List<EmployeeVO> employees;
public EmployeeBO() {
employees = new ArrayList<>();
EmployeeVO david = new EmployeeVO(1, "David");
EmployeeVO scott = new EmployeeVO(2, "Scott");
EmployeeVO jessica = new EmployeeVO(3, "Jessica");
employees.add(david);
employees.add(scott);
employees.add(jessica);
}
public void deleteEmployee(EmployeeVO employee) {
employees.remove(employee.getEmployeeId());
System.out.println("Employee with ID: " + employee.getEmployeeId() + " was successfully deleted.");
}
public List<EmployeeVO> getAllEmployees() {
return employees;
}
public EmployeeVO getEmployee(int id) {
return employees.get(id);
}
public void updateEmployee(EmployeeVO employee) {
employees.get(employee.getEmployeeId()).setName(employee.getName());
System.out.println("Employee with ID: " + employee.getEmployeeId() + " successfully updated.");
}
}
And to illustrate the point of the pattern:
public class Main {
public static void main(String[] args) {
EmployeeBO employeeBo = new EmployeeBO();
for(EmployeeVO employee : employeeBo.getAllEmployees()) {
System.out.println("Employee: |" + employee.getName() + ", ID: " + employee.getEmployeeId() + "|");
}
EmployeeVO employee = employeeBo.getAllEmployees().get(0);
employee.setName("Andrew");
employeeBo.updateEmployee(employee);
employee = employeeBo.getEmployee(0);
System.out.println("Employee: |" + employee.getName() + ", ID: " + employee.getEmployeeId() + "|");
}
}
Running this piece of code will yield:
Employee: |David, ID: 1|
Employee: |Scott, ID: 2|
Employee: |Jessica, ID: 3|
Employee with ID: 1 successfully updated.
Employee: |Andrew, ID: 1|
Conclusion
With this, all J2EE Design Patterns in Java are fully covered, with working examples.
This concludes our short series of articles on Java Design Patterns. If you found this one informative and missed any of the previous ones, feel free to check them out too: