Skip to content

Commit 7652b11

Browse files
Kevinyl3iluwatar
andauthored
new pattern: Issue#1264: Implemented Composite-View Pattern (iluwatar#1923)
* 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ä <[email protected]>
1 parent 07ee94d commit 7652b11

20 files changed

+978
-0
lines changed

composite-view/README.md

+325
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,325 @@
1+
---
2+
layout: pattern
3+
title: Composite View
4+
folder: composite-view
5+
permalink: /patterns/composite-view/
6+
categories: Structural
7+
language: en
8+
tags:
9+
- Enterprise Integration Pattern
10+
- Presentation
11+
---
12+
13+
## Name
14+
**Composite View**
15+
16+
## Intent
17+
The purpose of the Composite View Pattern is to increase re-usability and flexibility when creating views for websites/webapps.
18+
This pattern seeks to decouple the content of the page from its layout, allowing changes to be made to either the content
19+
or layout of the page without impacting the other. This pattern also allows content to be easily reused across different views easily.
20+
21+
## Explanation
22+
Real World Example
23+
> A news site wants to display the current date and news to different users
24+
> based on that user's preferences. The news site will substitute in different news feed
25+
> components depending on the user's interest, defaulting to local news.
26+
27+
In Plain Words
28+
> Composite View Pattern is having a main view being composed of smaller subviews.
29+
> The layout of this composite view is based on a template. A View-manager then decides which
30+
> subviews to include in this template.
31+
32+
Wikipedia Says
33+
> Composite views that are composed of multiple atomic subviews. Each component of
34+
> the template may be included dynamically into the whole and the layout of the page may be managed independently of the content.
35+
> This solution provides for the creation of a composite view based on the inclusion and substitution of
36+
> modular dynamic and static template fragments.
37+
> It promotes the reuse of atomic portions of the view by encouraging modular design.
38+
39+
**Programmatic Example**
40+
41+
Since this is a web development pattern, a server is required to demonstrate it.
42+
This example uses Tomcat 10.0.13 to run the servlet, and this programmatic example will only work with Tomcat 10+.
43+
44+
Firstly there is `AppServlet` which is an `HttpServlet` that runs on Tomcat 10+.
45+
```java
46+
public class AppServlet extends HttpServlet {
47+
private String msgPartOne = "<h1>This Server Doesn't Support";
48+
private String msgPartTwo = "Requests</h1>\n"
49+
+ "<h2>Use a GET request with boolean values for the following parameters<h2>\n"
50+
+ "<h3>'name'</h3>\n<h3>'bus'</h3>\n<h3>'sports'</h3>\n<h3>'sci'</h3>\n<h3>'world'</h3>";
51+
52+
private String destination = "newsDisplay.jsp";
53+
54+
public AppServlet() {
55+
56+
}
57+
58+
@Override
59+
public void doGet(HttpServletRequest req, HttpServletResponse resp)
60+
throws ServletException, IOException {
61+
RequestDispatcher requestDispatcher = req.getRequestDispatcher(destination);
62+
ClientPropertiesBean reqParams = new ClientPropertiesBean(req);
63+
req.setAttribute("properties", reqParams);
64+
requestDispatcher.forward(req, resp);
65+
}
66+
67+
@Override
68+
public void doPost(HttpServletRequest req, HttpServletResponse resp)
69+
throws ServletException, IOException {
70+
resp.setContentType("text/html");
71+
PrintWriter out = resp.getWriter();
72+
out.println(msgPartOne + " Post " + msgPartTwo);
73+
74+
}
75+
76+
@Override
77+
public void doDelete(HttpServletRequest req, HttpServletResponse resp)
78+
throws ServletException, IOException {
79+
resp.setContentType("text/html");
80+
PrintWriter out = resp.getWriter();
81+
out.println(msgPartOne + " Delete " + msgPartTwo);
82+
83+
}
84+
85+
@Override
86+
public void doPut(HttpServletRequest req, HttpServletResponse resp)
87+
throws ServletException, IOException {
88+
resp.setContentType("text/html");
89+
PrintWriter out = resp.getWriter();
90+
out.println(msgPartOne + " Put " + msgPartTwo);
91+
92+
}
93+
}
94+
95+
```
96+
This servlet is not part of the pattern, and simply forwards GET requests to the correct JSP.
97+
PUT, POST, and DELETE requests are not supported and will simply show an error message.
98+
99+
The view management in this example is done via a javabean class: `ClientPropertiesBean`, which stores user preferences.
100+
```java
101+
public class ClientPropertiesBean implements Serializable {
102+
103+
private static final String WORLD_PARAM = "world";
104+
private static final String SCIENCE_PARAM = "sci";
105+
private static final String SPORTS_PARAM = "sport";
106+
private static final String BUSINESS_PARAM = "bus";
107+
private static final String NAME_PARAM = "name";
108+
109+
private static final String DEFAULT_NAME = "DEFAULT_NAME";
110+
private boolean worldNewsInterest;
111+
private boolean sportsInterest;
112+
private boolean businessInterest;
113+
private boolean scienceNewsInterest;
114+
private String name;
115+
116+
public ClientPropertiesBean() {
117+
worldNewsInterest = true;
118+
sportsInterest = true;
119+
businessInterest = true;
120+
scienceNewsInterest = true;
121+
name = DEFAULT_NAME;
122+
123+
}
124+
125+
public ClientPropertiesBean(HttpServletRequest req) {
126+
worldNewsInterest = Boolean.parseBoolean(req.getParameter(WORLD_PARAM));
127+
sportsInterest = Boolean.parseBoolean(req.getParameter(SPORTS_PARAM));
128+
businessInterest = Boolean.parseBoolean(req.getParameter(BUSINESS_PARAM));
129+
scienceNewsInterest = Boolean.parseBoolean(req.getParameter(SCIENCE_PARAM));
130+
String tempName = req.getParameter(NAME_PARAM);
131+
if (tempName == null || tempName == "") {
132+
tempName = DEFAULT_NAME;
133+
}
134+
name = tempName;
135+
}
136+
// getters and setters generated by Lombok
137+
}
138+
```
139+
This javabean has a default constructor, and another that takes an `HttpServletRequest`.
140+
This second constructor takes the request object, parses out the request parameters which contain the
141+
user preferences for different types of news.
142+
143+
The template for the news page is in `newsDisplay.jsp`
144+
```html
145+
<html>
146+
<head>
147+
<style>
148+
h1 { text-align: center;}
149+
h2 { text-align: center;}
150+
h3 { text-align: center;}
151+
.centerTable {
152+
margin-left: auto;
153+
margin-right: auto;
154+
}
155+
table {border: 1px solid black;}
156+
tr {text-align: center;}
157+
td {text-align: center;}
158+
</style>
159+
</head>
160+
<body>
161+
<%ClientPropertiesBean propertiesBean = (ClientPropertiesBean) request.getAttribute("properties");%>
162+
<h1>Welcome <%= propertiesBean.getName()%></h1>
163+
<jsp:include page="header.jsp"></jsp:include>
164+
<table class="centerTable">
165+
166+
<tr>
167+
<td></td>
168+
<% if(propertiesBean.isWorldNewsInterest()) { %>
169+
<td><%@include file="worldNews.jsp"%></td>
170+
<% } else { %>
171+
<td><%@include file="localNews.jsp"%></td>
172+
<% } %>
173+
<td></td>
174+
</tr>
175+
<tr>
176+
<% if(propertiesBean.isBusinessInterest()) { %>
177+
<td><%@include file="businessNews.jsp"%></td>
178+
<% } else { %>
179+
<td><%@include file="localNews.jsp"%></td>
180+
<% } %>
181+
<td></td>
182+
<% if(propertiesBean.isSportsInterest()) { %>
183+
<td><%@include file="sportsNews.jsp"%></td>
184+
<% } else { %>
185+
<td><%@include file="localNews.jsp"%></td>
186+
<% } %>
187+
</tr>
188+
<tr>
189+
<td></td>
190+
<% if(propertiesBean.isScienceNewsInterest()) { %>
191+
<td><%@include file="scienceNews.jsp"%></td>
192+
<% } else { %>
193+
<td><%@include file="localNews.jsp"%></td>
194+
<% } %>
195+
<td></td>
196+
</tr>
197+
</table>
198+
</body>
199+
</html>
200+
```
201+
This JSP page is the template. It declares a table with three rows, with one component in the first row,
202+
two components in the second row, and one component in the third row.
203+
204+
The scriplets in the file are part of the
205+
view management strategy that include different atomic subviews based on the user preferences in the Javabean.
206+
207+
Here are two examples of the mock atomic subviews used in the composite:
208+
`businessNews.jsp`
209+
```html
210+
<html>
211+
<head>
212+
<style>
213+
h2 { text-align: center;}
214+
table {border: 1px solid black;}
215+
tr {text-align: center;}
216+
td {text-align: center;}
217+
</style>
218+
</head>
219+
<body>
220+
<h2>
221+
Generic Business News
222+
</h2>
223+
<table style="margin-right: auto; margin-left: auto">
224+
<tr>
225+
<td>Stock prices up across the world</td>
226+
<td>New tech companies to invest in</td>
227+
</tr>
228+
<tr>
229+
<td>Industry leaders unveil new project</td>
230+
<td>Price fluctuations and what they mean</td>
231+
</tr>
232+
</table>
233+
</body>
234+
</html>
235+
```
236+
`localNews.jsp`
237+
```html
238+
<html>
239+
<body>
240+
<div style="text-align: center">
241+
<h3>
242+
Generic Local News
243+
</h3>
244+
<ul style="list-style-type: none">
245+
<li>
246+
Mayoral elections coming up in 2 weeks
247+
</li>
248+
<li>
249+
New parking meter rates downtown coming tomorrow
250+
</li>
251+
<li>
252+
Park renovations to finish by the next year
253+
</li>
254+
<li>
255+
Annual marathon sign ups available online
256+
</li>
257+
</ul>
258+
</div>
259+
</body>
260+
</html>
261+
```
262+
The results are as such:
263+
264+
1) The user has put their name as `Tammy` in the request parameters and no preferences:
265+
![alt text](etc/images/noparam.PNG)
266+
2) The user has put their name as `Johnny` in the request parameters and has a preference for world, business, and science news:
267+
![alt text](etc/images/threeparams.PNG)
268+
269+
The different subviews such as `worldNews.jsp`, `businessNews.jsp`, etc. are included conditionally
270+
based on the request parameters.
271+
272+
**How To Use**
273+
274+
To try this example, make sure you have Tomcat 10+ installed.
275+
Set up your IDE to build a WAR file from the module and deploy that file to the server
276+
277+
IntelliJ:
278+
279+
Under `Run` and `edit configurations` Make sure Tomcat server is one of the run configurations.
280+
Go to the deployment tab, and make sure there is one artifact being built called `composite-view:war exploded`.
281+
If not present, add one.
282+
283+
Ensure that the artifact is being built from the content of the `web` directory and the compilation results of the module.
284+
Point the output of the artifact to a convenient place. Run the configuration and view the landing page,
285+
follow instructions on that page to continue.
286+
287+
## Class diagram
288+
289+
![alt text](./etc/composite_view.png)
290+
291+
The class diagram here displays the Javabean which is the view manager.
292+
The views are JSP's held inside the web directory.
293+
294+
## Applicability
295+
296+
This pattern is applicable to most websites that require content to be displayed dynamically/conditionally.
297+
If there are components that need to be re-used for multiple views, or if the project requires reusing a template,
298+
or if it needs to include content depending on certain conditions, then this pattern is a good choice.
299+
300+
## Known uses
301+
302+
Most modern websites use composite views in some shape or form, as they have templates for views and small atomic components
303+
that are included in the page dynamically. Most modern Javascript libraries, like React, support this design pattern
304+
with components.
305+
306+
## Consequences
307+
**Pros**
308+
* Easy to re-use components
309+
* Change layout/content without affecting the other
310+
* Reduce code duplication
311+
* Code is more maintainable and modular
312+
313+
**Cons**
314+
* Overhead cost at runtime
315+
* Slower response compared to directly embedding elements
316+
* Increases potential for display errors
317+
318+
## Related patterns
319+
* [Composite (GoF)](https://java-design-patterns.com/patterns/composite/)
320+
* [View Helper](https://www.oracle.com/java/technologies/viewhelper.html)
321+
322+
## Credits
323+
* [Core J2EE Patterns - Composite View](https://www.oracle.com/java/technologies/composite-view.html)
324+
* [Composite View Design Pattern – Core J2EE Patterns](https://www.dineshonjava.com/composite-view-design-pattern/)
325+
+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
@startuml
2+
package com.iluwatar.compositeview {
3+
class ClientPropertiesBean {
4+
- BUSINESS_PARAM : String {static}
5+
- DEFAULT_NAME : String {static}
6+
- NAME_PARAM : String {static}
7+
- SCIENCE_PARAM : String {static}
8+
- SPORTS_PARAM : String {static}
9+
- WORLD_PARAM : String {static}
10+
- businessInterest : boolean
11+
- name : String
12+
- scienceNewsInterest : boolean
13+
- sportsInterest : boolean
14+
- worldNewsInterest : boolean
15+
+ ClientPropertiesBean()
16+
+ ClientPropertiesBean(req : HttpServletRequest)
17+
+ getName() : String
18+
+ isBusinessInterest() : boolean
19+
+ isScienceNewsInterest() : boolean
20+
+ isSportsInterest() : boolean
21+
+ isWorldNewsInterest() : boolean
22+
+ setBusinessInterest(businessInterest : boolean)
23+
+ setName(name : String)
24+
+ setScienceNewsInterest(scienceNewsInterest : boolean)
25+
+ setSportsInterest(sportsInterest : boolean)
26+
+ setWorldNewsInterest(worldNewsInterest : boolean)
27+
}
28+
}
29+
@enduml

composite-view/etc/composite_view.png

18.6 KB
Loading

composite-view/etc/images/noparam.PNG

70.8 KB
Loading
76.8 KB
Loading

0 commit comments

Comments
 (0)