Upgrade to Pro — share decks privately, control downloads, hide ads and more …

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!

Guillaume Laforge

December 12, 2014
Tweet

More Decks by Guillaume Laforge

Other Decks in Programming

Transcript

  1. Guillaume Laforge 

    @glaforge
    with Style

    View Slide

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

    View Slide

  3. 11+ years
    of experience
    with Groovy

    View Slide

  4. What style?

    View Slide

  5. A touch
    of code
    style

    View Slide

  6. A bit of puzzlers

    View Slide

  7. Some glittering
    gems

    View Slide

  8. And advice from
    the wise

    View Slide

  9. View Slide

  10. View Slide

  11. View Slide

  12. <=>

    View Slide

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

    View Slide

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

    View Slide

  15. View Slide

  16. View Slide

  17. I’ll have to run to King
    Cross Saint Pancras right
    after the talk!

    View Slide

  18. View Slide

  19. View Slide

  20. @glaforge
    No semicolons!
    14
    class HelloLondon {

    private String message = 'Hello London';

    String greet(String name) {

    "$message, $name speaking";

    } 

    }


    print new HelloLondon().greet('Guillaume');

    View Slide

  21. @glaforge
    No semicolons!
    14
    class HelloLondon {

    private String message = 'Hello London';

    String greet(String name) {

    "$message, $name speaking";

    } 

    }


    print new HelloLondon().greet('Guillaume');

    View Slide

  22. @glaforge
    No semicolons!
    15
    class HelloLondon {

    private String message = 'Hello London'

    String greet(String name) {

    "$message, $name speaking"

    } 

    }


    print new HelloLondon().greet('Guillaume')

    View Slide

  23. View Slide

  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"

    } 

    }

    View Slide

  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

    View Slide

  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"

    } 

    }

    View Slide

  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!

    View Slide

  28. Till ‘def’ do us part!

    View Slide

  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

    } 

    }


    View Slide

  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

    } 

    }


    View Slide

  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

    } 

    }


    View Slide

  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

    View Slide

  33. View Slide

  34. @glaforge
    Return is optional too
    25
    String greet() { 

    return "$message, $name speaking"

    }

    View Slide

  35. @glaforge
    Return is optional too
    25
    String greet() { 

    return "$message, $name speaking"

    }

    View Slide

  36. @glaforge
    Return is optional too
    26
    String greet() { 

    "$message, $name speaking"

    }

    View Slide

  37. @glaforge
    Return is optional too
    26
    String greet() { 

    "$message, $name speaking"

    }
    But return is fine for
    early returns, or for
    clarity of intent

    View Slide

  38. @glaforge
    Optional return for if / else
    27
    int color(String name) {

    if (name == 'red')

    1

    else if (name == 'green')

    2

    else

    -1

    }

    View Slide

  39. @glaforge
    Optional return for switch / case
    28
    int color(String name) {

    switch (name) {

    case 'red': 1; break

    case 'green': 2; break

    default: -1

    }

    }

    View Slide

  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…

    View Slide

  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')

    View Slide

  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

    View Slide

  43. View Slide

  44. @glaforge
    Omit parentheses
    31
    println('Hello, World')

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  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"

    }

    View Slide

  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, {…})

    View Slide

  52. View Slide

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

    View Slide

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

    View Slide

  55. View Slide

  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

    }

    }

    View Slide

  57. @glaforge
    No need for dumb getters and setters
    41
    class Person {

    String name

    int age

    }

    View Slide

  58. @glaforge
    No need for dumb getters and setters
    41
    class Person {

    String name

    int age

    }
    Icing on the cake: add
    @Immutable or @Canonical

    View Slide

  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

    View Slide

  60. View Slide

  61. @glaforge
    Use the named-argument constructor
    44





    def p = new Person(name: 'Guillaume', 

    age: 37)




    View Slide

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


    @Builder

    class Person {

    String name

    int age

    }


    Person.builder()
    .name('Guillaume')
    .age(37)
    .build()

    View Slide

  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

    View Slide

  64. View Slide

  65. @glaforge
    Equals and ==
    47
    status != null && status.equals(Control.STATUS_COMPLETED)

    View Slide

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

    View Slide

  67. View Slide

  68. @glaforge
    Use single quotes for String constants
    50
    def msg = 'Groovy Rocks!'

    View Slide

  69. View Slide

  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.*/

    View Slide

  71. View Slide

  72. @glaforge
    The Groovy Development Kit
    54

    View Slide

  73. @glaforge
    The Groovy Development Kit
    54

    View Slide

  74. @glaforge
    The Groovy Development Kit
    54
    Know your GDK!
    So much groovy-ness in there!

    View Slide

  75. View Slide

  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"

    }

    View Slide

  77. View Slide

  78. @glaforge
    Import aliasing
    58
    import java.util.List as juList

    import java.awt.List as aList


    import javax.swing.WindowConstants as WC

    View Slide

  79. @glaforge
    Import aliasing with static imports too
    59
    import static java.lang.Integer.parseInt as atoi


    assert atoi('123') == 123

    View Slide

  80. View Slide

  81. @glaforge
    Groovy Truth
    61
    assert !( null )

    assert !( "" )

    assert !( [] )

    assert !( 0 )
    assert new Object()

    assert "string"

    assert [1, 2, 3]

    assert 1234

    View Slide

  82. @glaforge
    Groovy Truth
    61
    assert !( null )

    assert !( "" )

    assert !( [] )

    assert !( 0 )
    assert new Object()

    assert "string"

    assert [1, 2, 3]

    assert 1234
    False

    View Slide

  83. @glaforge
    Groovy Truth
    61
    assert !( null )

    assert !( "" )

    assert !( [] )

    assert !( 0 )
    assert new Object()

    assert "string"

    assert [1, 2, 3]

    assert 1234
    False
    True

    View Slide

  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)

    View Slide

  85. View Slide

  86. View Slide

  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

    View Slide

  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!

    View Slide

  89. View Slide

  90. @glaforge
    Power Assert, coming from Spock
    66
    def (a, b, c, d) = [10, 20, 30, 610]

    assert a + b * c == d - 40

    View Slide

  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

    View Slide

  92. View Slide

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


    def result = name ?: "Unknown"

    View Slide

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


    def result = name ?: "Unknown"
    Groovy Truth 

    at play here

    View Slide

  95. View Slide

  96. @glaforge
    Catch ‘any’ exception
    70
    try {

    something()

    } catch (any) {

    // ...

    }

    View Slide

  97. @glaforge
    Catch ‘any’ exception
    70
    try {

    something()

    } catch (any) {

    // ...

    } Someone suggested me:
    catch(emAll)

    View Slide

  98. View Slide

  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

    View Slide

  100. Thanks for
    your attention!

    View Slide

  101. Q & A

    View Slide

  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

    View Slide