* initial commit, created package, README, pom, and directory structure. * Issue#1264, continue working on JavaBeans, added getters, setters, and private fields. Created test file for JavaBeans. * set up junit for tests folder. * Issue#1264, set up local server and added web-application framework to composite-view to allow the JSP to run on a local Tomcat container. Wrote unit tests for Java-bean class, working on JSP pages. * Issue#1264, Added forwarding functionality to servlet and main composite view page. * Issue#1264, Finished composite view template in newsDisplay.jsp and created atomic sub-view components in businessNews.jsp, header.jsp, localNews.jsp, scienceNews.jsp, sportsNews.jsp, worldNews.jsp. Composite view page renders correctly, atomic views are inserted in and substituted in the template page depending on request parameters. * Issue#1264, Added all views, updated README.md with documentation. * Issue#1264, updated README.md, moved images folder into etc folder. * Issue#1264, removed build artifacts from tracked files. * Issue#1264, updated README.md * Issue#1264, updated README.md * Issue#1264, removed unused import, made AppServlet class final, changed to .equals() for string comparison. * Issue#1264, in AppServlet, put the output writing into try blocks to ensure writers are closed. * Issue#1264, added tests for Servlet, coverage up to 100%, used lombok to reduce boilerplate setters and getter, updated README.md with better grammar, appropriate tags and links to related patterns. Updated pom.xml to get rid of superfluous lines. * Issue#1264, made changes as requested in README.md. Co-authored-by: Ilkka Seppälä <iluwatar@users.noreply.github.com>
326 lines
11 KiB
Markdown
326 lines
11 KiB
Markdown
---
|
||
layout: pattern
|
||
title: Composite View
|
||
folder: composite-view
|
||
permalink: /patterns/composite-view/
|
||
categories: Structural
|
||
language: en
|
||
tags:
|
||
- Enterprise Integration Pattern
|
||
- Presentation
|
||
---
|
||
|
||
## Name
|
||
**Composite View**
|
||
|
||
## Intent
|
||
The purpose of the Composite View Pattern is to increase re-usability and flexibility when creating views for websites/webapps.
|
||
This pattern seeks to decouple the content of the page from its layout, allowing changes to be made to either the content
|
||
or layout of the page without impacting the other. This pattern also allows content to be easily reused across different views easily.
|
||
|
||
## Explanation
|
||
Real World Example
|
||
> A news site wants to display the current date and news to different users
|
||
> based on that user's preferences. The news site will substitute in different news feed
|
||
> components depending on the user's interest, defaulting to local news.
|
||
|
||
In Plain Words
|
||
> Composite View Pattern is having a main view being composed of smaller subviews.
|
||
> The layout of this composite view is based on a template. A View-manager then decides which
|
||
> subviews to include in this template.
|
||
|
||
Wikipedia Says
|
||
> Composite views that are composed of multiple atomic subviews. Each component of
|
||
> the template may be included dynamically into the whole and the layout of the page may be managed independently of the content.
|
||
> This solution provides for the creation of a composite view based on the inclusion and substitution of
|
||
> modular dynamic and static template fragments.
|
||
> It promotes the reuse of atomic portions of the view by encouraging modular design.
|
||
|
||
**Programmatic Example**
|
||
|
||
Since this is a web development pattern, a server is required to demonstrate it.
|
||
This example uses Tomcat 10.0.13 to run the servlet, and this programmatic example will only work with Tomcat 10+.
|
||
|
||
Firstly there is `AppServlet` which is an `HttpServlet` that runs on Tomcat 10+.
|
||
```java
|
||
public class AppServlet extends HttpServlet {
|
||
private String msgPartOne = "<h1>This Server Doesn't Support";
|
||
private String msgPartTwo = "Requests</h1>\n"
|
||
+ "<h2>Use a GET request with boolean values for the following parameters<h2>\n"
|
||
+ "<h3>'name'</h3>\n<h3>'bus'</h3>\n<h3>'sports'</h3>\n<h3>'sci'</h3>\n<h3>'world'</h3>";
|
||
|
||
private String destination = "newsDisplay.jsp";
|
||
|
||
public AppServlet() {
|
||
|
||
}
|
||
|
||
@Override
|
||
public void doGet(HttpServletRequest req, HttpServletResponse resp)
|
||
throws ServletException, IOException {
|
||
RequestDispatcher requestDispatcher = req.getRequestDispatcher(destination);
|
||
ClientPropertiesBean reqParams = new ClientPropertiesBean(req);
|
||
req.setAttribute("properties", reqParams);
|
||
requestDispatcher.forward(req, resp);
|
||
}
|
||
|
||
@Override
|
||
public void doPost(HttpServletRequest req, HttpServletResponse resp)
|
||
throws ServletException, IOException {
|
||
resp.setContentType("text/html");
|
||
PrintWriter out = resp.getWriter();
|
||
out.println(msgPartOne + " Post " + msgPartTwo);
|
||
|
||
}
|
||
|
||
@Override
|
||
public void doDelete(HttpServletRequest req, HttpServletResponse resp)
|
||
throws ServletException, IOException {
|
||
resp.setContentType("text/html");
|
||
PrintWriter out = resp.getWriter();
|
||
out.println(msgPartOne + " Delete " + msgPartTwo);
|
||
|
||
}
|
||
|
||
@Override
|
||
public void doPut(HttpServletRequest req, HttpServletResponse resp)
|
||
throws ServletException, IOException {
|
||
resp.setContentType("text/html");
|
||
PrintWriter out = resp.getWriter();
|
||
out.println(msgPartOne + " Put " + msgPartTwo);
|
||
|
||
}
|
||
}
|
||
|
||
```
|
||
This servlet is not part of the pattern, and simply forwards GET requests to the correct JSP.
|
||
PUT, POST, and DELETE requests are not supported and will simply show an error message.
|
||
|
||
The view management in this example is done via a javabean class: `ClientPropertiesBean`, which stores user preferences.
|
||
```java
|
||
public class ClientPropertiesBean implements Serializable {
|
||
|
||
private static final String WORLD_PARAM = "world";
|
||
private static final String SCIENCE_PARAM = "sci";
|
||
private static final String SPORTS_PARAM = "sport";
|
||
private static final String BUSINESS_PARAM = "bus";
|
||
private static final String NAME_PARAM = "name";
|
||
|
||
private static final String DEFAULT_NAME = "DEFAULT_NAME";
|
||
private boolean worldNewsInterest;
|
||
private boolean sportsInterest;
|
||
private boolean businessInterest;
|
||
private boolean scienceNewsInterest;
|
||
private String name;
|
||
|
||
public ClientPropertiesBean() {
|
||
worldNewsInterest = true;
|
||
sportsInterest = true;
|
||
businessInterest = true;
|
||
scienceNewsInterest = true;
|
||
name = DEFAULT_NAME;
|
||
|
||
}
|
||
|
||
public ClientPropertiesBean(HttpServletRequest req) {
|
||
worldNewsInterest = Boolean.parseBoolean(req.getParameter(WORLD_PARAM));
|
||
sportsInterest = Boolean.parseBoolean(req.getParameter(SPORTS_PARAM));
|
||
businessInterest = Boolean.parseBoolean(req.getParameter(BUSINESS_PARAM));
|
||
scienceNewsInterest = Boolean.parseBoolean(req.getParameter(SCIENCE_PARAM));
|
||
String tempName = req.getParameter(NAME_PARAM);
|
||
if (tempName == null || tempName == "") {
|
||
tempName = DEFAULT_NAME;
|
||
}
|
||
name = tempName;
|
||
}
|
||
// getters and setters generated by Lombok
|
||
}
|
||
```
|
||
This javabean has a default constructor, and another that takes an `HttpServletRequest`.
|
||
This second constructor takes the request object, parses out the request parameters which contain the
|
||
user preferences for different types of news.
|
||
|
||
The template for the news page is in `newsDisplay.jsp`
|
||
```html
|
||
<html>
|
||
<head>
|
||
<style>
|
||
h1 { text-align: center;}
|
||
h2 { text-align: center;}
|
||
h3 { text-align: center;}
|
||
.centerTable {
|
||
margin-left: auto;
|
||
margin-right: auto;
|
||
}
|
||
table {border: 1px solid black;}
|
||
tr {text-align: center;}
|
||
td {text-align: center;}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<%ClientPropertiesBean propertiesBean = (ClientPropertiesBean) request.getAttribute("properties");%>
|
||
<h1>Welcome <%= propertiesBean.getName()%></h1>
|
||
<jsp:include page="header.jsp"></jsp:include>
|
||
<table class="centerTable">
|
||
|
||
<tr>
|
||
<td></td>
|
||
<% if(propertiesBean.isWorldNewsInterest()) { %>
|
||
<td><%@include file="worldNews.jsp"%></td>
|
||
<% } else { %>
|
||
<td><%@include file="localNews.jsp"%></td>
|
||
<% } %>
|
||
<td></td>
|
||
</tr>
|
||
<tr>
|
||
<% if(propertiesBean.isBusinessInterest()) { %>
|
||
<td><%@include file="businessNews.jsp"%></td>
|
||
<% } else { %>
|
||
<td><%@include file="localNews.jsp"%></td>
|
||
<% } %>
|
||
<td></td>
|
||
<% if(propertiesBean.isSportsInterest()) { %>
|
||
<td><%@include file="sportsNews.jsp"%></td>
|
||
<% } else { %>
|
||
<td><%@include file="localNews.jsp"%></td>
|
||
<% } %>
|
||
</tr>
|
||
<tr>
|
||
<td></td>
|
||
<% if(propertiesBean.isScienceNewsInterest()) { %>
|
||
<td><%@include file="scienceNews.jsp"%></td>
|
||
<% } else { %>
|
||
<td><%@include file="localNews.jsp"%></td>
|
||
<% } %>
|
||
<td></td>
|
||
</tr>
|
||
</table>
|
||
</body>
|
||
</html>
|
||
```
|
||
This JSP page is the template. It declares a table with three rows, with one component in the first row,
|
||
two components in the second row, and one component in the third row.
|
||
|
||
The scriplets in the file are part of the
|
||
view management strategy that include different atomic subviews based on the user preferences in the Javabean.
|
||
|
||
Here are two examples of the mock atomic subviews used in the composite:
|
||
`businessNews.jsp`
|
||
```html
|
||
<html>
|
||
<head>
|
||
<style>
|
||
h2 { text-align: center;}
|
||
table {border: 1px solid black;}
|
||
tr {text-align: center;}
|
||
td {text-align: center;}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<h2>
|
||
Generic Business News
|
||
</h2>
|
||
<table style="margin-right: auto; margin-left: auto">
|
||
<tr>
|
||
<td>Stock prices up across the world</td>
|
||
<td>New tech companies to invest in</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Industry leaders unveil new project</td>
|
||
<td>Price fluctuations and what they mean</td>
|
||
</tr>
|
||
</table>
|
||
</body>
|
||
</html>
|
||
```
|
||
`localNews.jsp`
|
||
```html
|
||
<html>
|
||
<body>
|
||
<div style="text-align: center">
|
||
<h3>
|
||
Generic Local News
|
||
</h3>
|
||
<ul style="list-style-type: none">
|
||
<li>
|
||
Mayoral elections coming up in 2 weeks
|
||
</li>
|
||
<li>
|
||
New parking meter rates downtown coming tomorrow
|
||
</li>
|
||
<li>
|
||
Park renovations to finish by the next year
|
||
</li>
|
||
<li>
|
||
Annual marathon sign ups available online
|
||
</li>
|
||
</ul>
|
||
</div>
|
||
</body>
|
||
</html>
|
||
```
|
||
The results are as such:
|
||
|
||
1) The user has put their name as `Tammy` in the request parameters and no preferences:
|
||

|
||
2) The user has put their name as `Johnny` in the request parameters and has a preference for world, business, and science news:
|
||

|
||
|
||
The different subviews such as `worldNews.jsp`, `businessNews.jsp`, etc. are included conditionally
|
||
based on the request parameters.
|
||
|
||
**How To Use**
|
||
|
||
To try this example, make sure you have Tomcat 10+ installed.
|
||
Set up your IDE to build a WAR file from the module and deploy that file to the server
|
||
|
||
IntelliJ:
|
||
|
||
Under `Run` and `edit configurations` Make sure Tomcat server is one of the run configurations.
|
||
Go to the deployment tab, and make sure there is one artifact being built called `composite-view:war exploded`.
|
||
If not present, add one.
|
||
|
||
Ensure that the artifact is being built from the content of the `web` directory and the compilation results of the module.
|
||
Point the output of the artifact to a convenient place. Run the configuration and view the landing page,
|
||
follow instructions on that page to continue.
|
||
|
||
## Class diagram
|
||
|
||

|
||
|
||
The class diagram here displays the Javabean which is the view manager.
|
||
The views are JSP's held inside the web directory.
|
||
|
||
## Applicability
|
||
|
||
This pattern is applicable to most websites that require content to be displayed dynamically/conditionally.
|
||
If there are components that need to be re-used for multiple views, or if the project requires reusing a template,
|
||
or if it needs to include content depending on certain conditions, then this pattern is a good choice.
|
||
|
||
## Known uses
|
||
|
||
Most modern websites use composite views in some shape or form, as they have templates for views and small atomic components
|
||
that are included in the page dynamically. Most modern Javascript libraries, like React, support this design pattern
|
||
with components.
|
||
|
||
## Consequences
|
||
**Pros**
|
||
* Easy to re-use components
|
||
* Change layout/content without affecting the other
|
||
* Reduce code duplication
|
||
* Code is more maintainable and modular
|
||
|
||
**Cons**
|
||
* Overhead cost at runtime
|
||
* Slower response compared to directly embedding elements
|
||
* Increases potential for display errors
|
||
|
||
## Related patterns
|
||
* [Composite (GoF)](https://java-design-patterns.com/patterns/composite/)
|
||
* [View Helper](https://www.oracle.com/java/technologies/viewhelper.html)
|
||
|
||
## Credits
|
||
* [Core J2EE Patterns - Composite View](https://www.oracle.com/java/technologies/composite-view.html)
|
||
* [Composite View Design Pattern – Core J2EE Patterns](https://www.dineshonjava.com/composite-view-design-pattern/)
|
||
|