I am fortunate, in a way, to be working on a website where the technology is dated and the code was not shown any “love and care” for a number of years. So why would anybody feel fortunate to be working on a website like this, well it is just so easy to make improvements and to feel the sense of achievement when you improve something.
I recently had an example where I was refactoring some of our servlets. As I was going through and tidying I noticed a number of similar pieces of code and html. To eliminate the duplication I moved the duplicated code into methods. Once I was complete with the refactoring, I was still not happy. A page needs to be built in a certain order and all our servlet pages should be built in the same manner.
As a result, this ended up being a neat example to utilise the Builder Design Pattern. I am not going to go into any detail about the pattern itself because there are a number of posts, for example this tutorial on the Builder Design Pattern and books, such as Design Patterns: Elements of Reusable Object-Oriented Software by the GOF. I simply want to provide an example of how this has simplified our building of servlets into a standard manner.
So essentially we would have required nearly 80 lines of code to generate the very simple example page below.
The code to create this page using our traditional coding style for servlets would have looked as per below.
package com.servlet.presentation; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.PrintWriter; public class ServletWithHTML extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/html"); PrintWriter out = response.getWriter(); out.println("<html> "); out.println("<head>"); out.println("<title>An example using a builder pattern to make the servlet code prettier</title>"); out.println("<link rel=\"stylesheet\" href=\"css\\reset.css\">"); out.println("<link rel=\"stylesheet\" href=\"css\\style.css\">"); out.println("</head> "); out.println("<body>"); out.println("<form>"); out.println("<div class=\"title\">Builder pattern to make servlet html creation easier to read</div> "); out.println("<div class=\"paragraph\">An example using a builder pattern to make the servlet code prettier</div>"); out.println("<dl>"); out.println("<dt class=\"twocolumn\"><label>"); out.println("Label text 1"); out.println("</label></dt>"); out.println("<dd class=\"twocolumn\"><p>"); out.println("Value text 1"); out.println("</p></dd>"); out.println("<dt class=\"twocolumn\"><label>"); out.println("Label text 2"); out.println("</label></dt>"); out.println("<dd class=\"twocolumn\"><p>"); out.println("Value text 2"); out.println("</p></dd>"); out.println("<dt class=\"twocolumn\"><label>"); out.println("Label text 3"); out.println("</label></dt>"); out.println("<dd class=\"twocolumn\"><p>"); out.println("Value text 3"); out.println("</p></dd>"); out.println("<dt class=\"twocolumn\"><label>"); out.println("Label text 4"); out.println("</label></dt>"); out.println("<dd class=\"twocolumn\"><p>"); out.println("Value text 4"); out.println("</p></dd>"); out.println("</dl>"); out.println("<div class=\"paragraph\">This is really hard to read</div>"); out.println("</form>"); out.println("</body>"); out.println("</html>"); } }
Even though this is a very simple example, reading the page is very difficult with all the html references and quotes. It is also not very descriptive in describing what the developer is trying to do.
So first off a FormBuilder was created to build the page and generate the associated html.
package com.servlet.builder; import java.io.PrintWriter; public class FormBuilder { private StringBuilder stringBuilder = new StringBuilder(); public FormBuilder() { stringBuilder.append("<form>"); } public FormBuilder title(String text) { div("title",text); return this; } public FormBuilder paragraph(String text) { div("paragraph",text); return this; } public FormBuilder twoColumnTable(TwoColumnTable twoColumnTable) { stringBuilder.append(twoColumnTable.toString()); return this; } public void toString(PrintWriter printWriter) { endForm(); printWriter.println(stringBuilder.toString()); } public String toString() { endForm(); return stringBuilder.toString(); } private void endForm() { stringBuilder.append("</form>"); } private void div(String style,String text){ stringBuilder.append("<div class=\"").append(style).append("\">"); stringBuilder.append(text); stringBuilder.append("</div>"); } }
To create the two column table in the example page a TwoColumnTable builder was created.
package com.servlet.builder; public class TwoColumnTable { StringBuilder stringBuilder = new StringBuilder(); public TwoColumnTable() { stringBuilder.append("<dl>"); } public TwoColumnTable row(String label, String text) { stringBuilder.append("<dt class=\"twocolumn\"><label>"); stringBuilder.append(label); stringBuilder.append("</label></dt>"); stringBuilder.append("<dd class=\"twocolumn\"><p>"); stringBuilder.append(text); stringBuilder.append("</p></dd>"); return this; } public String toString() { stringBuilder.append("</dl>"); return stringBuilder.toString(); } }
The nice thing with this is that some relationship is maintained between the html elements. In this example only a form can have a TwoColumnTable and row can only be contained in the TwoColumnTable.
Now with the FormBuilder and TwoColumnTableBuilder in place, our java code for the servlet would look like below.
package com.servlet.presentation; import com.servlet.builder.FormBuilder; import com.servlet.builder.TwoColumnTable; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.PrintWriter; public class ServletUsingBuilder extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/html"); PrintWriter out = response.getWriter(); out.println("<html> "); out.println("<head>"); out.println("<title>An example using a builder pattern to make the servlet code prettier</title>"); out.println("<link rel=\"stylesheet\" href=\"css\\reset.css\">"); out.println("<link rel=\"stylesheet\" href=\"css\\style.css\">"); out.println("</head> "); out.println("<body>"); new FormBuilder() .title("Builder pattern to make servlet html creation easier to read") .paragraph("An example using a builder pattern to make the servlet code prettier") .twoColumnTable(new TwoColumnTable() .row("Label text 1", "Value text 1") .row("Label text 2", "Value text 2") .row("Label text 3", "Value text 3") .row("Label text 4", "Value text 4")) .paragraph("This is really much easier to read") .toString(out); out.println("</body>"); out.println("</html>"); } }
So 26 fewer lines of code to output exactly the same html. That is nearly a 40% line count improvement on a very simple example.
Writing code to output standard html that is repeated throughout the site in this fashion provides the following benefits:
- Firstly, the code is a lot cleaner and easier to read.
- The code is more descriptive.
- A lot less code needs to be written.
- The html is generated consistently. We don’t have to remember all the html and css tags.
- The code becomes testable – for example we can write tests for the builder and if we make any changes to the html we can validate it is correct.
- Changes to the html we want to generate are made in a single location.
- The compiler provides support in ensuring tags are contained only within allowed elements.
The full source for this example is available at https://github.com/craigew/HtmlBuilder. In the near future I will add tests to the project to illustrate how this can aid with testability.