© Adam L. Davis 2019
Adam L. DavisLearning Groovy 3https://doi.org/10.1007/978-1-4842-5058-7_6

6. Groovy Design Patterns

Adam L. Davis1 
(1)
New York, NY, USA
 

Design patterns are a great way to make your code functional, readable, and extensible. There are some patterns that are easier and require less code in Groovy compared to Java.

Strategy Pattern

Imagine you have three different methods for finding totals as follows:
 1   def totalPricesLessThan10(prices) {
 2           int total = 0
 3           for (int price : prices)
 4                   if (price < 10) total += price
 5           total
 6   }
 7    def totalPricesMoreThan10(prices) {
 8               int total = 0
 9               for (int price : prices)
10                       if (price > 10) total += price
11               total
12   }
13   def  totalPrices(prices) {
14           int total = 0
15           for (int price : prices)
16                   total += price
17           total
18   }
A lot of code is duplicated in this case. There’s only one small thing that changes in each of these methods. In Groovy, you can use a closure parameter instead of three different methods so you can have the following:
1   def totalPrices(prices, selector) {
2           int total = 0
3           for (int price : prices)
4                   if (selector(price)) total += price
5           total
6   }
Now you have a method, totalPrices(prices, selector), where selector is a closure. Also, you can put the closure outside of the method parameters in a method call if it’s the last parameter. So you can call this method in the following ways to achieve the desired results:
1   totalPrices(prices) { it < 10 }
2   totalPrices(prices) { it > 10 }
3   totalPrices(prices) { true }

This not only makes your code more concise, it’s also easier to read and extend.

Meta-Programming

Groovy meta-programming means you can add functionality (fields and methods) to any class or interface at runtime. This allows you to add helper methods to commonly used classes or interfaces to make your code more concise and readable.

Meta-Class

You can add commonly used functionality using the metaClass in a dynamic Groovy project (without using @CompileStatic or @TypeChecked). For example, if you often need to split text into words, you could add a method to the String class using the following code:
String.metaClass.words = { -> split(/ +/) }
def text = "the lazy brown fox"
text.words() // Result: ['the', 'lazy', 'brown', 'fox']
For another example, let’s say you’re writing a javax.servlet.Filter and you get and set session attributes a lot. You could do the following to virtually add methods to the HttpSession interface:
1   HttpSession.metaClass.getAt = { key -> getAttribute(key) }
2   HttpSession.metaClass.putAt = {
3       key, value -> setAttribute(key, value)
4   }
This allows the following syntax for setting and getting attributes of your session:
1   def  session = request.session
2   session['my_id'] = '123' // calls putAt('my_id', '123')
3   def  id = session['my_id'] // calls getAt('my_id')

Categories

Category is one of the many meta-programming techniques available in Groovy. A Category is a class that can be used to add functionality to existing classes. It can be useful when you don’t want to mess with a class for the whole application, but only want special treatment for a limited section of the code.

To make a Category, you create some static methods that have at least one parameter of a particular type (e.g., an integer). When the Category is used, that type (the type of the first parameter) appears to have those methods in addition to its previously defined methods. The object instance on which the method is called is used as the first parameter.

For example, Groovy has the TimeCategory1 Category built-in for manipulating dates and times. This lets you add and subtract any arbitrary length of time. For example:
1   import groovy.time.TimeCategory
2   def now = new Date()
3   println now
4   use(TimeCategory) {
5       nextWeekPlusTenHours = now + 1.week + 10.hours
6   }
7   println nextWeekPlusTenHours
In this case, TimeCategory adds a bunch of methods to the Integer class . For example, some of the method signatures look like the following:
1   static Duration getDays(Integer self)
2   static TimeDuration getHours(Integer self)
3   static TimeDuration getMinutes(Integer self)
4   static DatumDependentDuration getMonths(Integer self)
5   static TimeDuration getSeconds(Integer self)

../images/426440_2_En_6_Chapter/426440_2_En_6_Figa_HTML.gif Exercise

Create your own Category class and then put it on GitHub.

Missing Methods

In Groovy you can intercept missing methods (methods that are called, but are not defined in the classic sense) using the methodMissing method. This can be a very useful design pattern when you want to dynamically define methods at runtime with a flexible method name and signature. You write the methodMissing signature as follows:
1   def methodMissing(String name, args) { /* your code */ }

The name parameter is the name of the missing method, and the args parameter is all of the arguments passed to that method (as an object array). You then can write whatever functionality you want this method to have, using the name and args parameters.

Next, to improve efficiency, you can intercept, cache, and invoke the called method. For example:
1   def methodMissing(String name, args) {
2           def impl = { /* your code */ }
3           getMetaClass()."$name" = impl
4           impl()
5   }

This implements the missing functionality and then adds it to the current class’s metaClass so that future calls go directly to the implementation instead of the methodMissing method . This might be useful if you expect the same missing methods to be called a lot.

To test this out, let’s implement a very simple method that just prints out the called method name:
def methodMissing(String name, args) {
    println "in methodMissing"
    def impl = { println name }
    getMetaClass()."$name" = impl
    impl()
}
Now we can call missing methods like the following:
wow()
thisworks()
wow()
The output shown below demonstrates that the second call to “wow” uses the metaClass method , not methodMissing:
in methodMissing
wow
in methodMissing
thisworks
wow

Delegation

Delegation is when a class has methods that directly call (method signature identical) methods of another class. This is hard in Java because it is difficult and time consuming to add methods to a class.

This is much easier with the @Delegate annotation . It’s like compile-time meta-programming. It automatically adds the methods of the delegate class to the current class.

For example:
 1   public class Person {
 2           def  eatDonuts() { println("yummy") }
 3   }
 4
 5   public class RoboCop  {
 6           @Delegate final Person person
 7
 8           public RoboCop(Person person) { this.person = person }
 9           public RoboCop() { this.person = new Person() }
10
11           def crushCars() {
12                   println("smash")
13           }
14   }
Although RoboCop does not have an eatDonuts() method , all of the methods of Person are added to RoboCop and delegated to person. This allows for the following usage:
1   def  person = new  RoboCop()
2   person.eatDonuts()
3   person.crushCars()

../images/426440_2_En_6_Chapter/426440_2_En_6_Figb_HTML.gif Exercise

Use @Delegate on a List property and use it to make a list that cannot have elements removed.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset