-
- Notifications
You must be signed in to change notification settings - Fork 27.4k
Issue#1264: Implemented Composite-View Pattern #1923
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
16 commits Select commit Hold shift + click to select a range
e31dcce initial commit, created package, README, pom, and directory structure.
Kevinyl3 f9b3d4a Issue#1264, continue working on JavaBeans, added getters, setters, an…
Kevinyl3 8f9ad82 set up junit for tests folder.
Kevinyl3 50ec720 Issue#1264, set up local server and added web-application framework t…
Kevinyl3 f63cceb Issue#1264, Added forwarding functionality to servlet and main compos…
Kevinyl3 4cd7f25 Issue#1264, Finished composite view template in newsDisplay.jsp and c…
Kevinyl3 864292d Issue#1264, Added all views, updated README.md with documentation.
Kevinyl3 011424d Issue#1264, updated README.md, moved images folder into etc folder.
Kevinyl3 0855d59 Issue#1264, removed build artifacts from tracked files.
Kevinyl3 4319b77 Issue#1264, updated README.md
Kevinyl3 1c4810b Issue#1264, updated README.md
Kevinyl3 32d7ed7 Issue#1264, removed unused import, made AppServlet class final, chang…
Kevinyl3 c1b9a4c Issue#1264, in AppServlet, put the output writing into try blocks to …
Kevinyl3 c6252f7 Issue#1264, added tests for Servlet, coverage up to 100%, used lombok…
Kevinyl3 1ef4b39 Issue#1264, made changes as requested in README.md.
Kevinyl3 6eb2861 Merge branch 'master' into Issue#1264
iluwatar File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,325 @@ | ||
| --- | ||
| 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/) | ||
| | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,29 @@ | ||
| @startuml | ||
| package com.iluwatar.compositeview { | ||
| class ClientPropertiesBean { | ||
| - BUSINESS_PARAM : String {static} | ||
| - DEFAULT_NAME : String {static} | ||
| - NAME_PARAM : String {static} | ||
| - SCIENCE_PARAM : String {static} | ||
| - SPORTS_PARAM : String {static} | ||
| - WORLD_PARAM : String {static} | ||
| - businessInterest : boolean | ||
| - name : String | ||
| - scienceNewsInterest : boolean | ||
| - sportsInterest : boolean | ||
| - worldNewsInterest : boolean | ||
| + ClientPropertiesBean() | ||
| + ClientPropertiesBean(req : HttpServletRequest) | ||
| + getName() : String | ||
| + isBusinessInterest() : boolean | ||
| + isScienceNewsInterest() : boolean | ||
| + isSportsInterest() : boolean | ||
| + isWorldNewsInterest() : boolean | ||
| + setBusinessInterest(businessInterest : boolean) | ||
| + setName(name : String) | ||
| + setScienceNewsInterest(scienceNewsInterest : boolean) | ||
| + setSportsInterest(sportsInterest : boolean) | ||
| + setWorldNewsInterest(worldNewsInterest : boolean) | ||
| } | ||
| } | ||
| @enduml |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit. This suggestion is invalid because no changes were made to the code. Suggestions cannot be applied while the pull request is closed. Suggestions cannot be applied while viewing a subset of changes. Only one suggestion per line can be applied in a batch. Add this suggestion to a batch that can be applied as a single commit. Applying suggestions on deleted lines is not supported. You must change the existing code in this line in order to create a valid suggestion. Outdated suggestions cannot be applied. This suggestion has been applied or marked resolved. Suggestions cannot be applied from pending reviews. Suggestions cannot be applied on multi-line comments. Suggestions cannot be applied while the pull request is queued to merge. Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.