Using Groovy Closures Instead of Template Method

Exclusive offer: get 50% off this eBook here
Groovy for Domain-Specific Languages

Groovy for Domain-Specific Languages — Save 50%

Extend and enhance your Java applications with Domain Specific Languages in Groovy

$26.99    $13.50
by Nirav Assar | December 2010 | Java Open Source

A good software developer always keeps the DRY principle in mind. Whenever code is duplicated, maintainability problems are introduced and the code base becomes difficult to understand. Java developers often rely on the template method pattern to reuse code. Groovy's closure programming concept is an alternative way to solve similar problems. Closures provide a concise way to pass around code without the ceremony involved with design patterns. This article by Nirav Assar will demonstrate how closures are used to embrace the DRY principle.

 

Groovy for Domain-Specific Languages

Groovy for Domain-Specific Languages

Extend and enhance your Java applications with Domain Specific Languages in Groovy

  • Build your own Domain Specific Languages on top of Groovy
  • Integrate your existing Java applications using Groovy-based Domain Specific Languages (DSLs)
  • Develop a Groovy scripting interface to Twitter
  • A step-by-step guide to building Groovy-based Domain Specific Languages that run seamlessly in the Java environment
        Read more about this book      

(For more resources on Groovy, see here.)

Template Method Pattern Overview

The template method pattern often applies during the thought "Well I have a piece of code that I want to use again, but I can't use it 100%. I want to change a few lines to make it useful." In general, using this pattern involves creating an abstract class and varying its implementation through abstract hook methods. Subclasses implement these abstract hook methods to solve their specific problem.

This approach is very effective and is used extensively in frameworks. However, closures provide an elegant solution.

Sample HttpBuilder Request

It is best to illustrate the closure approach with an example. Recently I was developing a consumer of REST webservices with HttpBuilder. With HttpBuilder, the client simply creates the class and issues an HTTP call. The framework waits for a response and provides hooks for processing.

Many of the requests being made were very similar to one another, only the URI was different. In addition, each request needed to process the returned XML differently, as the XML received would vary. I wanted to use the same request code, but vary the XML processing. To summarize the problem:

  • HttpBuilder code should be reused
  • Different URIs should be sent out with the same HttpBuilder code
  • Different XML should be processed with the same HttpBuilder code

Here is my first draft of HttpBuilder code. Note the call to convertXmlToCompanyDomainObject(xml).

static String URI_PREFIX = '/someApp/restApi/'

private List issueHttpBuilderRequest(RequestObject requestObj, String uriPath) {
def http = new HTTPBuilder("http://localhost:8080/")
def parsedObjectsFromXml = []

http.request(Method.POST, ContentType.XML) { req ->
// set uri path on the delegate
uri.path = URI_PREFIX + uriPath
uri.query = [
company: requestObj.company,
date: requestObj.date
type: requestObj.type
]
headers.'User-Agent' = 'Mozilla/5.0'

// when response is a success, parse the gpath xml
response.success = { resp, xml ->
assert resp.statusLine.statusCode == 200
// store the list
parsedObjectsFromXml = convertXmlToCompanyDomainObject(xml)
}

// called only for a 404 (not found) status code:
response.'404' = { resp ->
log.info 'HTTP status code: 404 Not found'
}
}
parsedObjectsFromXml
}

private List convertXmlToCompanyDomainObject(GPathResult xml) {
def list = []
// .. implementation to parse the xml and turn into objects
}

As you can see, URI is passed as a parameter to issueHttpBuilderRequest. This solves the problem of sending different URIs, but what about parsing the different XML formats that are returned?

Using Template Method Pattern

The following diagram illustrates applying the template method pattern to this problem. In summary, we need to move the issueHttpBuilderRequest code to an abstract class, and provide an abstract method convertXmlToDomainObjects(). Subclasses would provide the appropriate XML conversion implementation.

Using Groovy Closures Instead of Template Method

Groovy for Domain-Specific Languages Extend and enhance your Java applications with Domain Specific Languages in Groovy
Published: June 2010
eBook Price: $26.99
Book Price: $44.99
See more
Select your format and quantity:

 

        Read more about this book      

(For more resources on Groovy, see here.)

Using Closures

Now for the closure approach. Without creating extra classes or using a formal pattern, we can solve the same problem. We can define two closures in the same class, and pass these closures into the issueHttpBuilderRequest method. The method will execute the closure when appropriate. After all, we want to identify the area of variation and insert some different code at that variation point.

Notice the added argument Closure convertXmlToDomainObjects to the issueHttpBuilderRequest method, along with two new closure definitions.

static String URI_PREFIX = '/someApp/restApi/'

private List issueHttpBuilderRequest(RequestObject requestObj, String uriPath, Closure

convertXmlToDomainObjects) {
def http = new HTTPBuilder("http://localhost:8080/")
def parsedObjectsFromXml = []

http.request(Method.POST, ContentType.XML) { req ->
// set uri path on the delegate
uri.path = URI_PREFIX + uriPath
uri.query = [
company: requestObj.company,
date: requestObj.date
type: requestObj.type
]
headers.'User-Agent' = 'Mozilla/5.0'

// when response is a success, parse the gpath xml
response.success = { resp, xml ->
assert resp.statusLine.statusCode == 200
log.info "My response handler got response: ${resp.statusLine}"
// store the list
parsedObjectsFromXml = convertXmlToDomainObjects(xml)
}

// called only for a 404 (not found) status code:
response.'404' = { resp ->
log.info 'HTTP status code: 404 Not found'
}
}
parsedObjectsFromXml
}

def convertXmlToCompanyDomainObject = { GPathResult xml ->
def list = []
// .. implementation to parse the xml and turn into objec
}

def convertXmlToCorporationObject = { GPathResult xml ->
def list = []
// .. implementation to parse the xml and turn into objec
}

Now we can call the issueHttpBuilderRequest method as follows:

def companyObjects = issueHttpBuilderRequest (request, 'companyInformation',

convertXmlToCompanyDomainObject)

def corporationObjects = issueHttpBuilderRequest (request, 'corporationInformation',

convertXmlToCorporationObject)

Summary

The template method pattern is an effective and common pattern solution. However, try using the closure approach and compare the different implementations. It's always good to have another tool in the arsenal.

On the other hand, you could achieve the same by copying and pasting code, or inserting only if statements to handle variances. Punt those options and go for the elegant approach. It's better for maintenance, easier on the eye for other developers, and makes our job easier in the long run.


Further resources on this subject:


 

Groovy for Domain-Specific Languages Extend and enhance your Java applications with Domain Specific Languages in Groovy
Published: June 2010
eBook Price: $26.99
Book Price: $44.99
See more
Select your format and quantity:

About the Author :


Nirav Assar is an independent consultant in the Dallas/Fort Worth area. He is co-partner at Solutionsfit, a consulting firm that assists enterprise with the development needs. Nirav has worked with several technologies professionally, such as Grails/Groovy, Seam, Spring, and Hibernate. His areas of expertise include agile methodologies, object-oriented design, test driven development, and refactoring. In his leisure time, Nirav enjoys reading, blogging, watching sports, and playing soccer. You can view his blog at Assar Java Consulting.

Books From Packt

Grails 1.1 Web Application Development
Grails 1.1 Web Application Development

PostgreSQL 9.0 High Performance
PostgreSQL 9.0 High Performance

Google App Engine Java and GWT Application   Development
Google App Engine Java and GWT Application Development

Python 2.6 Text Processing: Beginners Guide
Python 2.6 Text Processing: Beginners Guide

Learning Ext JS 3.2
Learning Ext JS 3.2

JBoss AS 5 Performance Tuning
JBoss AS 5 Performance Tuning

PHP jQuery Cookbook
PHP jQuery Cookbook

Drupal 7
Drupal 7

No votes yet

Post new comment

CAPTCHA
This question is for testing whether you are a human visitor and to prevent automated spam submissions.
q
Q
4
b
V
8
Enter the code without spaces and pay attention to upper/lower case.
Code Download and Errata
Packt Anytime, Anywhere
Register Books
Print Upgrades
eBook Downloads
Video Support
Contact Us
Awards Voting Nominations Previous Winners
Judges Open Source CMS Hall Of Fame CMS Most Promising Open Source Project Open Source E-Commerce Applications Open Source JavaScript Library Open Source Graphics Software
Resources
Open Source CMS Hall Of Fame CMS Most Promising Open Source Project Open Source E-Commerce Applications Open Source JavaScript Library Open Source Graphics Software