The concept of closures is an important part of the Groovy language. A closure resembles a function or method in many aspects of programming. For example a closure can take arguments, can have a return value, and can be executed from one or many clients. A closure is essentially a block of code that is defined and executed a later point. A closure can be assigned to a variable or remain anonymous. You can create a closure, assign it to a variable, and then pass it around your program like any other variable. For a more thorough explanation, see Groovy Formal Definition.
Use Groovy Closures
Closures enable the creation of creating concise, clean code when applied correctly. Although all programming problems can be solved without closures, a problem solved using closures will most likely contain less code, avoid repetition, and be more elegant. This article will focus on some simple and common uses of closures. It is directed towards newcomers to Groovy, typically Java developers coming over to the dark side. We will discover how closures can be used for:
- Looping and Iteration
- Collection Methods
- Mock Objects
Sample Domain
In order to demonstrate the examples, I will create a simple domain. Let's take a restaurant as a sample domain to apply our problems. Say we have a restaurant that serves customers, and obviously it has tables for diners. Thus, we have a simple domain to consider.
class Restaurant { def tables = [] } class Table { def location def numOfDiners }
Looping and Iteration
The most common use of closures is for iterating over a set of objects. In Java, in order to loop through objects you must create an iterator, use an index, or a for-each construct. Next, with a handle to an object you can perform some operation. Think of a closure as taking the code inside of a for loop and applying it to each element.
From our domain, let's try to print the number of diners at each table.
package com.solutionsfit.closures class Restaurant { def tables = [] def createTables() { def table1 = new Table(location:"a1", numOfDiners:2) def table2 = new Table(location:"a2", numOfDiners:3) def table3 = new Table(location:"b1", numOfDiners:1) def table4 = new Table(location:"b2", numOfDiners:8) tables = [table1, table2, table3, table4] } def printNumOfDinersAtEachTable() { println "Number diners at each table - using it" tables.each ({ println it.numOfDiners }) println "Number diners at each table - using var table" tables.each ({ table -> println table.numOfDiners }) } }Now let's print out the locations of the tables in the restaurant.
def printTableLocations() { println "Locations of tables" tables.each ({ println it.location }) }Collection Methods
Groovy Collections have some fantastics methods that make dealing with lists very simple. Most take in a closure, which makes it easy to apply an operation to each element under the context of the collection method. For example, say we wanted to collect the number of diners at each table in a list and return it, instead of just printing it out. The collect method creates a list containing each result of the closure passed in.
def collectNumOfDinersAtEachTable() { println "Returning a collection with the number of diners" def dinersList = tables.collect ({ it.numOfDiners }) println dinersList }Now let's presume for sake of demonstration that we needed a report containing the location of each table, and the number of diners at each table. This information should be returned as a map. We have a list of tables, and the information inside a table must be turned into a map. The inject method is ideal for this. It is best suited for problems that are additive in nature, meaning you start with a solution and you keep adding to it with an operation on each element.
def createMapOfTableLocationAndNumOfDiners() { println "Convert the tables into a map of location and num of diners" def map = tables.inject([:]) { resultMap, table -> resultMap[table.location] = table.numOfDiners resultMap } println map }
Mock Objects
Closures can also be used to mock objects during testing. This is a huge simplification over using mock frameworks in many situations. Although mock frameworks are very useful, many times a unit test may just need a simple mock object. Closures are a great tool in this regard.
A closure can be defined as a map, with the key serving as the name of a method you would like to mock out. Then you can substitute the closure map in the place of the real object and use the mock in a unit test.
To apply to our domain, say the restaurant needs to pay yearly licence fees to runs its business. In particular the business needs to a pay liquor license and a fire inspection fee. The liquor license is a percentage of sales, and the fire inspection fee is a percentage of the square footage of the property. Here is how we calculate the license fees:
package com.solutionsfit.closures class Restaurant { def tables = [] LicenseCalculator stateLicenceCalculator def previousYearSales def squareFoot ... def calculateLicenseFees() { def liquorFee = stateLicenceCalculator.liquorLicense() * previousYearSales def fireFee = stateLicenceCalculator.fireInspection() * squareFoot def total = liquorFee + fireFee; total } }Notice how the Restaurant delegates to the stateLicenceCalculator to get the percentage tax. In testing, we can mock this out with closures, then inject it into the restaurant.
package com.solutionsfit.closures; import groovy.util.GroovyTestCase class RestaurantTest extends GroovyTestCase { void testCalculateLicenseFees() { def restaurant = new Restaurant(previousYearSales: 90000, squareFoot:5000) // create mock from closure def mockLicenseCalculator = [liquorLicense: { return 0.11}, fireInspection: {return 0.08}] as LicenseCalculator // inject mock into restaurant restaurant.stateLicenceCalculator = mockLicenseCalculator def fees = restaurant.calculateLicenseFees() assertEquals(10300.00, fees) } }Code Sample
For an eclipse project of the source code from this blog, go to power-of-closures.
Interesting Article
ReplyDeleteSpring Hibernate Online Training | Hibernate Training in Chennai
Hibernate Online Training | Java Online Training | Java EE Online Training
Continue the good work
ReplyDeletesexy girls
Thanks for sharing this informative content , Great work
ReplyDeleteLeanpitch provides online training in Advanced Scrum Master during this lockdown period everyone can use it wisely.
Advanced Scrum Master Training Online
This is the great blog post. Yellowstone Coat
ReplyDelete