Groovy with Style -- Groovy Grails eXchange 2014

Groovy with Style -- Groovy Grails eXchange 2014

What should "idiomatic" Groovy code look like? What are the good practices in terms of syntax style, typing preferences? What are the nice shortcuts to be aware of to be more productive? Guillaume Laforge will answer those questions along the way!

137d3908243acfc30e126615d59d4e6d?s=128

Guillaume Laforge

December 12, 2014
Tweet

Transcript

  1. Guillaume Laforge 
 @glaforge with Style

  2. Guillaume Laforge Groovy project lead @glaforge http://google.com/+GuillaumeLaforge http://glaforge.appspot.com

  3. 11+ years of experience with Groovy

  4. What style?

  5. A touch of code style

  6. A bit of puzzlers

  7. Some glittering gems

  8. And advice from the wise

  9. None
  10. None
  11. None
  12. <=>

  13. @glaforge http://beta.groovy-lang.org/style-guide.html 10

  14. @glaforge http://beta.groovy-lang.org/style-guide.html 10

  15. None
  16. None
  17. I’ll have to run to King Cross Saint Pancras right

    after the talk!
  18. None
  19. None
  20. @glaforge No semicolons! 14 class HelloLondon {
 private String message

    = 'Hello London';
 String greet(String name) {
 "$message, $name speaking";
 } 
 }
 
 print new HelloLondon().greet('Guillaume');
  21. @glaforge No semicolons! 14 class HelloLondon {
 private String message

    = 'Hello London';
 String greet(String name) {
 "$message, $name speaking";
 } 
 }
 
 print new HelloLondon().greet('Guillaume');
  22. @glaforge No semicolons! 15 class HelloLondon {
 private String message

    = 'Hello London'
 String greet(String name) {
 "$message, $name speaking"
 } 
 }
 
 print new HelloLondon().greet('Guillaume')
  23. None
  24. @glaforge Public by default 17 public class HelloLondon {
 private

    String message = 'Hello London'
 private String name
 
 public HelloLondon(String name) {
 this.name = name
 }
 public String greet() {
 return "$message, $name speaking"
 } 
 }
  25. @glaforge Public by default 18 class HelloLondon {
 private String

    message = 'Hello London'
 private String name
 
 HelloLondon(String name) {
 this.name = name
 }
 String greet() {
 return "$message, $name speaking"
 } 
 } Methods and classes are public by default
  26. @glaforge Fields and static fields require visibility 19 class HelloLondon

    {
 static String message = 'Hello London'
 String name
 
 HelloLondon(String name) {
 this.name = name
 }
 String greet() {
 return "$message, $name speaking"
 } 
 }
  27. @glaforge Fields and static fields require visibility 19 class HelloLondon

    {
 static String message = 'Hello London'
 String name
 
 HelloLondon(String name) {
 this.name = name
 }
 String greet() {
 return "$message, $name speaking"
 } 
 } You created both an instance property and a static property!
  28. Till ‘def’ do us part!

  29. @glaforge Get rid of the useless ‘def’ 21 class HelloLondon

    {
 private String message = 'Hello London'
 private def String name
 def HelloLondon(def String name) {
 this.name = name
 }
 public def String greet() {
 def String result = "$message, $name speaking"
 return result
 } 
 }

  30. @glaforge Get rid of the useless ‘def’ 21 class HelloLondon

    {
 private String message = 'Hello London'
 private def String name
 def HelloLondon(def String name) {
 this.name = name
 }
 public def String greet() {
 def String result = "$message, $name speaking"
 return result
 } 
 }

  31. @glaforge Get rid of the useless ‘def’ 22 class HelloLondon

    {
 private String message = 'Hello London'
 private String name
 HelloLondon(String name) {
 this.name = name
 }
 String greet() {
 String result = "$message, $name speaking"
 return result
 } 
 }

  32. @glaforge Get rid of the useless ‘def’ 23 class HelloLondon

    {
 private String message = 'Hello London'
 private String name
 HelloLondon(String name) {
 this.name = name
 }
 String greet() {
 def result = "$message, $name speaking"
 return result
 } 
 }
 But it’s fine to use ‘def’ for local variables
  33. None
  34. @glaforge Return is optional too 25 String greet() { 


    return "$message, $name speaking"
 }
  35. @glaforge Return is optional too 25 String greet() { 


    return "$message, $name speaking"
 }
  36. @glaforge Return is optional too 26 String greet() { 


    "$message, $name speaking"
 }
  37. @glaforge Return is optional too 26 String greet() { 


    "$message, $name speaking"
 } But return is fine for early returns, or for clarity of intent
  38. @glaforge Optional return for if / else 27 int color(String

    name) {
 if (name == 'red')
 1
 else if (name == 'green')
 2
 else
 -1
 }
  39. @glaforge Optional return for switch / case 28 int color(String

    name) {
 switch (name) {
 case 'red': 1; break
 case 'green': 2; break
 default: -1
 }
 }
  40. @glaforge Optional return for switch / case 28 int color(String

    name) {
 switch (name) {
 case 'red': 1; break
 case 'green': 2; break
 default: -1
 }
 } Yeah, yeah, ugly semicolon and break…
  41. @glaforge Optional return for try / catch 29 boolean isInt(String

    s) {
 try {
 Integer.parseInt(s)
 true
 } catch (NumberFormatException nfe) {
 false
 } finally {
 println 'done'
 }
 }
 
 assert isInt('123') && assert !isInt('abc')
  42. @glaforge Optional return for try / catch 29 boolean isInt(String

    s) {
 try {
 Integer.parseInt(s)
 true
 } catch (NumberFormatException nfe) {
 false
 } finally {
 println 'done'
 }
 }
 
 assert isInt('123') && assert !isInt('abc') ‘finally’ blocks don’t return a value
  43. None
  44. @glaforge Omit parentheses 31 println('Hello, World')

  45. @glaforge Omit parentheses 32 println 'Hello, World'

  46. @glaforge Command chains 33 move forward at 3.km/h

  47. @glaforge Command chains 33 move forward at 3.km/h Parsed as:

    move(forward).at(3.getKm().div(h))
  48. @glaforge Command chains and named arguments 34 check that: wine

    tastes good
  49. @glaforge Command chains and named arguments 34 check that: wine

    tastes good Parsed as: check(that: wine).tastes(good)
  50. @glaforge Trailing closure argument gets out 35 void unless(boolean b,

    Closure c) {
 if (!b) c()
 }
 
 def (speed, limit) = [80, 90]
 
 unless(speed > limit) {
 println "speed up a notch"
 }
  51. @glaforge Trailing closure argument gets out 35 void unless(boolean b,

    Closure c) {
 if (!b) c()
 }
 
 def (speed, limit) = [80, 90]
 
 unless(speed > limit) {
 println "speed up a notch"
 } Parsed as: unless(speed > limit, {…})
  52. None
  53. @glaforge Class as first-class citizen 37 connection.doPost("${BASE_URI}/submit", params, ResourcesResponse.class)

  54. @glaforge Class as first-class citizen 38 connection.doPost("${BASE_URI}/submit", params, ResourcesResponse)

  55. None
  56. @glaforge No need for dumb getters and setters 40 class

    Person {
 private String name
 private int age
 
 void setName(String name) {
 this.name = name
 }
 String getName() {
 this.name
 }
 void setAge(int age) {
 this.age = age
 }
 int getAge() {
 this.age
 }
 }
  57. @glaforge No need for dumb getters and setters 41 class

    Person {
 String name
 int age
 }
  58. @glaforge No need for dumb getters and setters 41 class

    Person {
 String name
 int age
 } Icing on the cake: add @Immutable or @Canonical
  59. @glaforge Use the property notation 42 class Person {
 String

    name
 int age
 }
 
 def p = new Person(name: 'Guillaume', 
 age: 36)
 assert p.name == 'Guillaume'
 
 p.age = 37
 assert p.age == 37
  60. None
  61. @glaforge Use the named-argument constructor 44 
 
 
 


    
 def p = new Person(name: 'Guillaume', 
 age: 37)
 
 
 

  62. @glaforge The @Builder transformation 45 import groovy.transform.builder.*
 
 @Builder
 class

    Person {
 String name
 int age
 }
 
 Person.builder() .name('Guillaume') .age(37) .build()
  63. @glaforge The @Builder transformation 45 import groovy.transform.builder.*
 
 @Builder
 class

    Person {
 String name
 int age
 }
 
 Person.builder() .name('Guillaume') .age(37) .build() Different strategies available: chained setters, external builder class, type-safe initializer
  64. None
  65. @glaforge Equals and == 47 status != null && status.equals(Control.STATUS_COMPLETED)

  66. @glaforge Equals and == 48 status == Control.STATUS_COMPLETED

  67. None
  68. @glaforge Use single quotes for String constants 50 def msg

    = 'Groovy Rocks!'
  69. None
  70. @glaforge Native syntax for data structures 52 def list =

    [1, 2, 3]
 def range = 'a'..'b'
 def map = [a: 1, b: 2, c: 3]
 def regex = ~/.*foo.*/
  71. None
  72. @glaforge The Groovy Development Kit 54

  73. @glaforge The Groovy Development Kit 54

  74. @glaforge The Groovy Development Kit 54 Know your GDK! So

    much groovy-ness in there!
  75. None
  76. @glaforge Switch / case on steroids 56 switch(obj) {
 case

    123: "number 123"; break
 case "abc": "string abc"; break
 case String: "is a string"; break
 case [1, 2, 3]: "in list"; break
 case ~/.*o+.*/: "regex match"; break
 case { it < 3 }: "closure criteria"; break
 default: "unknown"
 }
  77. None
  78. @glaforge Import aliasing 58 import java.util.List as juList
 import java.awt.List

    as aList
 
 import javax.swing.WindowConstants as WC
  79. @glaforge Import aliasing with static imports too 59 import static

    java.lang.Integer.parseInt as atoi
 
 assert atoi('123') == 123
  80. None
  81. @glaforge Groovy Truth 61 assert !( null )
 assert !(

    "" )
 assert !( [] )
 assert !( 0 ) assert new Object()
 assert "string"
 assert [1, 2, 3]
 assert 1234
  82. @glaforge Groovy Truth 61 assert !( null )
 assert !(

    "" )
 assert !( [] )
 assert !( 0 ) assert new Object()
 assert "string"
 assert [1, 2, 3]
 assert 1234 False
  83. @glaforge Groovy Truth 61 assert !( null )
 assert !(

    "" )
 assert !( [] )
 assert !( 0 ) assert new Object()
 assert "string"
 assert [1, 2, 3]
 assert 1234 False True
  84. @glaforge Customize the truth with asBoolean() 62 class Account {


    String name
 boolean disabled = false
 
 boolean asBoolean() { !disabled }
 }
 
 assert new Account(name: 'current')
 assert !new Account(name: 'old', disabled: true)
  85. None
  86. None
  87. @glaforge Safe navigation 64 // Java if (order != null)

    {
 if (order.getCustomer() != null) {
 if (order.getCustomer().getAddress() != null) {
 System.out.println(order.getCustomer().getAddress());
 }
 }
 }
 // Groovy println order?.customer?.address
  88. @glaforge Safe navigation 64 // Java if (order != null)

    {
 if (order.getCustomer() != null) {
 if (order.getCustomer().getAddress() != null) {
 System.out.println(order.getCustomer().getAddress());
 }
 }
 }
 // Groovy println order?.customer?.address Also give more meaningful NPEs!
  89. None
  90. @glaforge Power Assert, coming from Spock 66 def (a, b,

    c, d) = [10, 20, 30, 610]
 assert a + b * c == d - 40
  91. @glaforge Power Assert, coming from Spock 66 def (a, b,

    c, d) = [10, 20, 30, 610]
 assert a + b * c == d - 40 Assertion failed:
 
 assert a + b * c == d - 40
 | | | | | | | |
 10| 20| 30| | 570
 610 600 | 610
 false
  92. None
  93. @glaforge Calling Elvis for default values 68 def result =

    name != null ? name : "Unknown"
 
 def result = name ?: "Unknown"
  94. @glaforge Calling Elvis for default values 68 def result =

    name != null ? name : "Unknown"
 
 def result = name ?: "Unknown" Groovy Truth 
 at play here
  95. None
  96. @glaforge Catch ‘any’ exception 70 try {
 something()
 } catch

    (any) {
 // ...
 }
  97. @glaforge Catch ‘any’ exception 70 try {
 something()
 } catch

    (any) {
 // ...
 } Someone suggested me: catch(emAll)
  98. None
  99. @glaforge Of the importance of contracts • Use explicit typing

    for – method signatures – properties & fields • Nice for – Java interoperability – documenting your APIs – IDE auto-completion and refactoring 72
  100. Thanks for your attention!

  101. Q & A

  102. @glaforge Picture credits Style: http://1hdwallpapers.com/wallpapers/my_style.jpg Eurostar: http://www.whatsupwhatson.com/wp-content/uploads/2014/04/Eurostar_3012_Waterloo.jpg Puzzle: http://joyinthecause.org/wp-content/uploads/2014/03/missing-puzzle-piece_00449088.jpg Gems:

    http://www.freewhd.com/blue-gems-wallpaper/ Wise: http://fc00.deviantart.net/fs70/i/2012/044/f/6/three_wise_men_color_by_csoro-d4pmlv2.jpg Ready: http://nameshapers.com/wp-content/uploads/2013/12/Social-Media-Coaching-for-CEOs-Getting-started.jpg Semicolon: http://www.freeimageslive.co.uk/image/view/3944/_original Till death to us part: https://mypencillines.files.wordpress.com/2013/04/img_0171.jpg Public: http://www.levelupliving.com/wp-content/uploads/2012/05/Fear-of-Public-Speaking-image.jpg Contract: https://www.icts.uiowa.edu/sites/default/files/contract.jpg Exception: http://www.quotescover.com/wp-content/uploads/Its-the-exception-that-proves__quotes-by-French-Proverb-77.png Banana: http://texturetaddka.com/wp-content/uploads/2011/10/DSC2008.jpg Elvis: http://natedsanders.com/blog/wp-content/uploads/2013/10/Elvis-Presley-Wallpaper-1280-x-960.jpeg Assert: http://www.itshouldjustworktm.com/wp-content/uploads/2012/03/assert-sign-photoshopped.jpg Compass: https://lh4.ggpht.com/WtY8QL0re9kMssF6q4RopDHC6QEpou7ybQo7qNRjn1X8Iu5uKUZv7huHA_PxPB8iGfs=h900 Safety vest: http://media.nautisports.com/catalog/product/image/gilet-de-sauvetage-gonflable-hydrostatique-hammar-pilot-275-n-avec-harnais-pro-rouge-nav_17452_web_1_1.jpg Class: http://upload.wikimedia.org/wikipedia/en/5/50/A_Class_in_Progress.jpg Aliasing: http://i.imgur.com/JrFly.png Light switch: http://www.hapihour.org/wp-content/uploads/2014/04/lighting-design-wall-light-winsome-designer-light-switches-australia-designer-light-switchesdesigner-light-switches-and-platesdesigner-light- switches-coversdesigner-light-switches-graydesign.jpg Steel structure: http://www.jingtabuilding.com/UploadFiles/sl6.jpg Quotes: http://g.fastcompany.net/multisite_files/fastcompany/poster/2013/09/3018353-poster-1280-quotess3.jpg Setter: http://upload.wikimedia.org/wikipedia/commons/b/b4/Rzesz%C3%B3w_Irish_Red_and_White_Setter_1pl.jpg Argument: http://www.familytreecounseling.com/kathysblog/wp-content/uploads/2014/01/arguing_couple.jpg Equal cupcake: http://static.communitytable.com/wp-content/uploads/2013/04/marriage-equality-cupcakes-ftr.jpg Obama truth: http://upload.wikimedia.org/wikipedia/commons/7/7c/Obama_swearing_in.JPG Quiz: http://a622.phobos.apple.com/us/r30/Purple/v4/df/a9/6e/dfa96e6c-ea9b-ee4c-4d6c-f9edf2775e1d/mzl.exfkkuro.png return: http://legacyofshadow.com/wp-content/uploads/2012/04/no-return-sign.jpg spock: http://trekcore.com/gallery/albums/spock09/spock_pb04.jpg London: http://www.100percentoptical.com/images/2014/10/london.jpg Tie fighter: http://img3.wikia.nocookie.net/__cb20090419001710/uncyclopedia/images/f/fe/Darthvader_tie_fighter.jpg 75