|
| 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 | + |
| 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 | + |
| 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 | + |
| 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 | + |
0 commit comments