Save 37% off PRO during our Black Friday Sale! »

Groovy with Style — GR8Conf Europe 2015

Groovy with Style — GR8Conf Europe 2015

Groovy with Style: tips, tricks, nuggets, style advice for the Groovy developers!

137d3908243acfc30e126615d59d4e6d?s=128

Guillaume Laforge

June 03, 2015
Tweet

Transcript

  1. Groovy with Style Guillaume Laforge @glaforge Restlet — the Web

    API platform Groovy project team
  2. We know about APIs!

  3. None
  4. GR8Conf Promo Code: cfgr8eu39 http://www.manning.com/koenig2/ GROOVY IN ACTION 2ND EDITION

  5. 11+ years of Groovy experience

  6. Quick intro to REST

  7. What style?

  8. None
  9. None
  10. None
  11. None
  12. None
  13. None
  14. None
  15. <=>

  16. 12 http://www.groovy-lang.org/style-guide.html

  17. 12 http://www.groovy-lang.org/style-guide.html

  18. None
  19. None
  20. None
  21. 15 No semicolons! class Hello {
 private String message =

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

    'Hello!';
 String greet(String name) {
 "$message, $name speaking";
 } 
 }
 
 print new Hello().greet('Guillaume');
  23. 16 No semicolons! class Hello {
 private String message =

    'Hello'
 String greet(String name) {
 "$message, $name speaking"
 } 
 }
 
 print new Hello().greet('Guillaume')
  24. None
  25. 18 Public by default public class Hello {
 private static

    String message = 'Hello'
 private String name
 
 public Hello(String name) {
 this.name = name
 }
 public String greet() {
 return "$message, $name speaking"
 } 
 }
  26. 19 Public by default class Hello {
 private static String

    message = 'Hello'
 private String name
 
 Hello(String name) {
 this.name = name
 }
 String greet() {
 return "$message, $name speaking"
 } 
 } Methods and classes are public by default
  27. 20 class Hello {
 static String message = 'Hello’
 String

    name
 
 Hello(String name) {
 this.name = name
 }
 String greet() {
 return "$message, $name speaking"
 } 
 } Fields and static fields require visibility
  28. 20 class Hello {
 static String message = 'Hello’
 String

    name
 
 Hello(String name) {
 this.name = name
 }
 String greet() {
 return "$message, $name speaking"
 } 
 } Fields and static fields require visibility You created both an instance property and a static property!
  29. None
  30. 22 Get rid of the useless ‘def’ class Hello {


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

  31. 22 Get rid of the useless ‘def’ class Hello {


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

  32. 23 Get rid of the useless ‘def’ class Hello {


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

  33. 24 Get rid of the useless ‘def’ class Hello {


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

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

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

    $name speaking"
 }
  38. 27 Return is optional too String greet() { 
 "$message,

    $name speaking"
 } But return is fine for early returns, or for clarity of intent
  39. 28 Optional return for if / else int color(String name)

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

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

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

    {
 try {
 Integer.parseInt(s)
 true
 } catch (NumberFormatException nfe) {
 false
 } finally {
 println 'done'
 }
 }
 
 assert isInt('123') && assert !isInt('abc')
  43. 30 Optional return for try / catch 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
  44. None
  45. 32 Omit parentheses println('Hello, World')

  46. 33 Omit parentheses println 'Hello, World'

  47. 34 Command chains move forward at 3.km/h

  48. 34 Command chains move forward at 3.km/h Parsed as: move(forward).at(3.getKm().div(h))

  49. 35 Command chains & named arguments check that: wine tastes

    good
  50. 35 Command chains & named arguments check that: wine tastes

    good Parsed as: check(that: wine).tastes(good)
  51. 36 Trailing closure argument gets out void unless(boolean b, Closure

    c) {
 if (!b) c()
 }
 
 def (speed, limit) = [80, 90]
 
 unless(speed > limit) {
 println "speed up a notch"
 }
  52. 36 Trailing closure argument gets out 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, {…})
  53. None
  54. 38 Class as first-class citizen connection.doPost("${BASE_URI}/submit", params, ResourcesResponse.class)

  55. 39 Class as first-class citizen connection.doPost("${BASE_URI}/submit", params, ResourcesResponse)

  56. None
  57. 41 No need for dumb getters and setters 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
 }
 }
  58. 42 No need for dumb getters and setters class Person

    {
 String name
 int age
 }
  59. 42 No need for dumb getters and setters class Person

    {
 String name
 int age
 } Icing on the cake: add @Immutable or @Canonical
  60. 43 Use the property notation 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
  61. None
  62. 45 Use the named-argument constructor def p = new Person(name:

    'Guillaume', 
 age: 37)
  63. 46 The @Builder transformation import groovy.transform.builder.*
 
 @Builder
 class Person

    {
 String name
 int age
 }
 
 Person.builder() .name('Guillaume') .age(37) .build()
  64. 46 The @Builder transformation 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
  65. None
  66. 48 Equals and == status != null && status.equals(Control.STATUS_COMPLETED)

  67. 49 Equals and == status == Control.STATUS_COMPLETED

  68. None
  69. 51 Use single quotes for String constants def msg =

    'Groovy Rocks!'
  70. None
  71. 53 Native syntax for data structures def list = [1,

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

  74. 55 The Groovy Development Kit

  75. 55 The Groovy Development Kit Know your GDK! So much

    groovy-ness in there!
  76. None
  77. 57 Switch / case on steroids 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"
 }
  78. None
  79. 59 Import aliasing import java.util.List as juList
 import java.awt.List as

    aList
 
 import javax.swing.WindowConstants as WC
  80. 60 Import aliasing with static imports too import static java.lang.Integer.parseInt

    as atoi
 
 assert atoi('123') == 123
  81. None
  82. 62 Groovy Truth assert !( null )
 assert !( ""

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

    )
 assert !( [] )
 assert !( 0 ) assert new Object()
 assert "string"
 assert [1, 2, 3]
 assert 1234 False
  84. 62 Groovy Truth assert !( null )
 assert !( ""

    )
 assert !( [] )
 assert !( 0 ) assert new Object()
 assert "string"
 assert [1, 2, 3]
 assert 1234 False True
  85. 63 Customize the truth with asBoolean() class Account {
 String

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


    if (order.getCustomer() != null) {
 if (order.getCustomer().getAddress() != null) {
 System.out.println(order.getCustomer().getAddress());
 }
 }
 }
 // Groovy println order?.customer?.address
  89. 65 Safe navigation // 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!
  90. None
  91. 67 Power Assert def (a, b, c, d) = [10,

    20, 30, 610]
 assert a + b * c == d - 40
  92. 67 Power Assert 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
  93. None
  94. 69 Calling Elvis for default values def result = name

    != null && name.length() > 0 ? name : "Unknown"
 
 def result = name ?: "Unknown"
  95. 69 Calling Elvis for default values def result = name

    != null && name.length() > 0 ? name : "Unknown"
 
 def result = name ?: "Unknown" Groovy Truth 
 at play here
  96. None
  97. 71 Catch ‘any’ exception try {
 something()
 } catch (any)

    {
 // ...
 }
  98. 71 Catch ‘any’ exception try {
 something()
 } catch (any)

    {
 // ...
 } Someone suggested me: catch(emAll)
  99. None
  100. 73 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
  101. Thanks for your attention!

  102. Q & A

  103. 76 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