Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Groovy Refactoring Patterns

267 views

Published on

Slides from GR8 Conf EU 2019 talk - "Groovy Refactoring Patterns". In this talk, I share the refactoring patterns I observed during Groovy development.

Published in: Software
  • Be the first to comment

  • Be the first to like this

Groovy Refactoring Patterns

  1. 1. Groovy Refactoring Patterns Naresha K @naresha_k https://blog.nareshak.com/
  2. 2. About me Developer, Coach, Consultant Founder & Organiser Bangalore Groovy User Group
  3. 3. Refactoring
  4. 4. noun: a change made to the internal structure of software to make it easier to understand and cheaper to modify without changing its observable behaviour verb: to restructure software by applying a series of refactorings without changing its observable behaviour. Refactoring https://refactoring.com/
  5. 5. Red Green Refactor TDD
  6. 6. Address Common Code Smells
  7. 7. A code smell is a surface indication that usually corresponds to a deeper problem in the system. The term was first coined by Kent Beck while helping me with my Refactoring book. ~ Martin Fowler https://martinfowler.com/bliki/CodeSmell.html
  8. 8. { Code Smells } Long Methods Duplicated Code Large Class Long Param List Primitive Obsession Data Class Inappropriate Intimacy
  9. 9. int calculateM1(int value1, int value2, int value3) { int maxValue if (value1 > value2 && value1 > value3) { maxValue = value1 } else if (value2 > value3) { maxValue = value2 } else { maxValue = value3 } maxValue + 1 } int calculateM2(int value1, int value2, int value3) { int maxValue if (value1 > value2 && value1 > value3) { maxValue = value1 } else if (value2 > value3) { maxValue = value2 } else { maxValue = value3 } maxValue * 2 }
  10. 10. int calculateM1(int value1, int value2, int value3) { int maxValue if (value1 > value2 && value1 > value3) { maxValue = value1 } else if (value2 > value3) { maxValue = value2 } else { maxValue = value3 } maxValue + 1 } int calculateM2(int value1, int value2, int value3) { int maxValue if (value1 > value2 && value1 > value3) { maxValue = value1 } else if (value2 > value3) { maxValue = value2 } else { maxValue = value3 } maxValue * 2 } Duplicate Code Reinventing the wheel
  11. 11. int calculateM1(int value1, int value2, int value3) { Math.max(value1, Math.max(value2, value3)) + 1 } int calculateM2(int value1, int value2, int value3) { Math.max(value1, Math.max(value2, value3)) * 2 }
  12. 12. int calculateM1(int value1, int value2, int value3) { int maxValue if (value1 > value2 && value1 > value3) { maxValue = value1 } else if (value2 > value3) { maxValue = value2 } else { maxValue = value3 } maxValue + 1 } int calculateM2(int value1, int value2, int value3) { int maxValue if (value1 > value2 && value1 > value3) { maxValue = value1 } else if (value2 > value3) { maxValue = value2 } else { maxValue = value3 } maxValue * 2 }
  13. 13. int calculateM1(int value1, int value2, int value3) { int maxValue if (value1 > value2 && value1 > value3) { maxValue = value1 } else if (value2 > value3) { maxValue = value2 } else { maxValue = value3 } maxValue + 1 } int calculateM1(int value1, int value2, int value3) { int maxValue = max(value1, value2, value3) maxValue + 1 } private int max(int value1, int value2, int value3) { int maxValue if (value1 > value2 && value1 > value3) { maxValue = value1 } else if (value2 > value3) { maxValue = value2 } else { maxValue = value3 } maxValue } Extract Method
  14. 14. int calculateM1(int value1, int value2, int value3) { int maxValue = max(value1, value2, value3) maxValue + 1 } private int max(int value1, int value2, int value3) { int maxValue if (value1 > value2 && value1 > value3) { maxValue = value1 } else if (value2 > value3) { maxValue = value2 } else { maxValue = value3 } maxValue } int calculateM2(int value1, int value2, int value3) { int maxValue if (value1 > value2 && value1 > value3) { maxValue = value1 } else if (value2 > value3) { maxValue = value2 } else { maxValue = value3 } maxValue * 2 }
  15. 15. int calculateM1(int value1, int value2, int value3) { int maxValue = max(value1, value2, value3) maxValue + 1 } private int max(int value1, int value2, int value3) { int maxValue if (value1 > value2 && value1 > value3) { maxValue = value1 } else if (value2 > value3) { maxValue = value2 } else { maxValue = value3 } maxValue } int calculateM2(int value1, int value2, int value3) { int maxValue = max(value1, value2, value3) maxValue * 2 }
  16. 16. private int max(int value1, int value2, int value3) { int maxValue if (value1 > value2 && value1 > value3) { maxValue = value1 } else if (value2 > value3) { maxValue = value2 } else { maxValue = value3 } maxValue } private int max(int value1, int value2, int value3) { Math.max(value1, Math.max(value2, value3)) }
  17. 17. private int max(int value1, int value2, int value3) { Math.max(value1, Math.max(value2, value3)) } int calculateM1(int value1, int value2, int value3) { int maxValue = max(value1, value2, value3) maxValue + 1 } int calculateM2(int value1, int value2, int value3) { int maxValue = max(value1, value2, value3) maxValue * 2 }
  18. 18. private int max(int value1, int value2, int value3) { Math.max(value1, Math.max(value2, value3)) } int calculateM1(int value1, int value2, int value3) { int maxValue = Math.max(value1, Math.max(value2, value3)) maxValue + 1 } int calculateM2(int value1, int value2, int value3) { int maxValue = Math.max(value1, Math.max(value2, value3)) maxValue * 2 } Inline Method
  19. 19. int calculateM1(int value1, int value2, int value3) { int maxValue = Math.max(value1, Math.max(value2, value3)) maxValue + 1 } int calculateM2(int value1, int value2, int value3) { int maxValue = Math.max(value1, Math.max(value2, value3)) maxValue * 2 } int calculateM1(int value1, int value2, int value3) { Math.max(value1, Math.max(value2, value3)) + 1 } int calculateM2(int value1, int value2, int value3) { Math.max(value1, Math.max(value2, value3)) * 2 } Inline Variable
  20. 20. Importance of small steps in refactoring
  21. 21. List<Integer> numbers = [1, 2, 3, 4, 5] for(number in numbers) { println number }
  22. 22. External Iterator To Internal Iterator
  23. 23. List<Integer> numbers = [1, 2, 3, 4, 5] for(number in numbers) { println number } List<Integer> numbers = [1, 2, 3, 4, 5] numbers.forEach { println it } List<Integer> numbers = [1, 2, 3, 4, 5] numbers.forEach(System.out.&println)
  24. 24. Refactoring to Idiomatic Groovy
  25. 25. def numbers = [1, 2, 3, 4, 5] for(number in numbers) { println number } def numbers = [1, 2, 3, 4, 5] numbers.each { println it }
  26. 26. def evenNumbers = [] numbers.each { number -> if (number % 2 == 0) { evenNumbers << number } } def evenNumbers = numbers .findAll { it % 2 == 0}
  27. 27. def sum = 0 numbers.each { sum += it } def sum = numbers.inject(0, { result, number -> result + number } )
  28. 28. List<Integer> numbers = [1, 2, 3, 4, 5] def result = [:] numbers.forEach { number -> result[number] = number * number } List<Integer> numbers = [1, 2, 3, 4, 5] def result = numbers.collectEntries { [it, it * it] }
  29. 29. List<Integer> numbers = [1, 2, 3, 4, 5] def evenNumbers = numbers.findAll { it % 2 == 0}
  30. 30. Extract Closure
  31. 31. List<Integer> numbers = [1, 2, 3, 4, 5] def evenNumbers = numbers.findAll { it % 2 == 0} List<Integer> numbers = [1, 2, 3, 4, 5] def evenNumbers = numbers.findAll({ it % 2 == 0 }) List<Integer> numbers = [1, 2, 3, 4, 5] def isEven = { it % 2 == 0 } def evenNumbers = numbers.findAll(isEven)
  32. 32. Inline Closure
  33. 33. List<Integer> numbers = [1, 2, 3, 4, 5] def isEven = { it % 2 == 0 } def evenNumbers = numbers.findAll(isEven) List<Integer> numbers = [1, 2, 3, 4, 5] def evenNumbers = numbers.findAll { it % 2 == 0}
  34. 34. def numbers = [1, 2, 3, 4, 5] def sumOfSuqaresOfEvenNumbers = 0 numbers.forEach { number -> if(number % 2 == 0 ){ sumOfSuqaresOfEvenNumbers += number * number } }
  35. 35. To Functional Style
  36. 36. def numbers = [1, 2, 3, 4, 5] def sumOfSuqaresOfEvenNumbers = 0 numbers.forEach { number -> if(number % 2 == 0 ){ sumOfSuqaresOfEvenNumbers += number * number } } def sumOfSuqaresOfEvenNumbers = numbers .findAll { it % 2 == 0 } .collect { it * it } .sum()
  37. 37. def numbers = [1, 2, 3, 4, 5] def sumOfSuqaresOfEvenNumbers = 0 numbers.forEach { number -> if(number % 2 == 0 ){ sumOfSuqaresOfEvenNumbers += number * number } } def square = { it * it } def sumOfSuqaresOfEvenNumbers = 0 numbers.forEach { number -> if(number % 2 == 0 ){ sumOfSuqaresOfEvenNumbers += square(number) } }
  38. 38. def square = { it * it } def sumOfSuqaresOfEvenNumbers = 0 numbers.forEach { number -> if(number % 2 == 0 ){ sumOfSuqaresOfEvenNumbers += square(number) } } def sumOfSuqaresOfEvenNumbers = 0 def evenNumbers = numbers.findAll { it % 2 == 0} evenNumbers.forEach { number -> sumOfSuqaresOfEvenNumbers += square(number) }
  39. 39. def sumOfSuqaresOfEvenNumbers = 0 def evenNumbers = numbers.findAll { it % 2 == 0} evenNumbers.forEach { number -> sumOfSuqaresOfEvenNumbers += square(number) } def sumOfSuqaresOfEvenNumbers = 0 def evenNumbers = numbers.findAll { it % 2 == 0} def squaresOfEvenNumbers = evenNumbers.collect(square) squaresOfEvenNumbers.forEach { number -> sumOfSuqaresOfEvenNumbers += number }
  40. 40. def square = { it * it } def evenNumbers = numbers.findAll { it % 2 == 0 } def squaresOfEvenNumbers = evenNumbers .collect ( square ) def sumOfSuqaresOfEvenNumbers = squaresOfEvenNumbers.sum() def sumOfSuqaresOfEvenNumbers = 0 def evenNumbers = numbers.findAll { it % 2 == 0} def squaresOfEvenNumbers = evenNumbers.collect(square) squaresOfEvenNumbers.forEach { number -> sumOfSuqaresOfEvenNumbers += number }
  41. 41. def square = { it * it } def evenNumbers = numbers.findAll { it % 2 == 0 } def squaresOfEvenNumbers = evenNumbers .collect ( square ) def sumOfSuqaresOfEvenNumbers = squaresOfEvenNumbers.sum() def sumOfSuqaresOfEvenNumbers = numbers .findAll { it % 2 == 0 } .collect (square) .sum() Inline
  42. 42. class Multiplier { private final int times Multiplier(int times) { this.times = times } int multiply(int number) { times * number } } Multiplier doubler = new Multiplier(2) Multiplier triple = new Multiplier(3) println doubler.multiply(10) println triple.multiply(10)
  43. 43. def multiply = { int times, int number -> times * number } def doubler = multiply.curry(2) def triple = multiply.curry(3) println doubler(10) println triple(10)
  44. 44. def numbers = [1, 2, 3, 4, 5, 6] println numbers .findAll { it % 2 == 0 } .find { it > 2 }
  45. 45. To Streams API
  46. 46. def numbers = [1, 2, 3, 4, 5, 6] println numbers .findAll { it % 2 == 0 } .find { it > 2 } def numbers = [1, 2, 3, 4, 5, 6] println numbers .stream() .filter { it % 2 == 0 } .filter { it > 2 } .findFirst() .orElse(null)
  47. 47. interface CanSwim { def swim() } class Fish implements CanSwim { def swim() { println "Fish Swimming" } } class Person implements CanSwim { def swim() { println "Person Swimming" } }
  48. 48. To Dynamic Groovy
  49. 49. interface CanSwim { def swim() } class Fish implements CanSwim { def swim() { println "Fish Swimming" } } class Person implements CanSwim { def swim() { println "Person Swimming" } } CanSwim swimmer = new Person() //new Fish() swimmer.swim()
  50. 50. interface CanSwim { def swim() } class Fish implements CanSwim { def swim() { println "Fish Swimming" } } class Person implements CanSwim { def swim() { println "Person Swimming" } } def swimmer = new Person() //new Fish() swimmer.swim()
  51. 51. class Employee { def employeeId def firstName def lastName def dateOfBirth def toSummaryString() { "$employeeId -> $firstname" } }
  52. 52. class Employee { def employeeId def firstName def lastName def dateOfBirth def toSummaryString() { "$employeeId -> $firstname" } }
  53. 53. To Static Groovy
  54. 54. class Employee { def employeeId def firstName def lastName def dateOfBirth def toSummaryString() { "$employeeId -> $firstname" } } Exception in thread "main" groovy.lang.MissingPropertyException: No such property: firstname for class
  55. 55. @TypeChecked class Employee { def employeeId def firstName def lastName def dateOfBirth def toSummaryString() { "$employeeId -> $firstname" } } Error:(17, 30) Groovyc: [Static type checking] - The variable [firstname] is undeclared.
  56. 56. @TypeChecked class Employee { def employeeId def firstName def lastName def dateOfBirth def toSummaryString() { "$employeeId -> $firstname" } } @TypeChecked class Employee { String employeeId String firstName String lastName LocalDateTime dateOfBirth def toSummaryString() { "$employeeId -> $firstname" } }
  57. 57. TypeChecked Vs CompileStatic
  58. 58. class Greeter { String message def greet() { message } } @TypeChecked def runTypeChecked(Greeter greeter) { println greeter.greet() } @CompileStatic def runCompileStatic(Greeter greeter) { println greeter.greet() }
  59. 59. def greeter = new Greeter(message: 'Good Morning') greeter.metaClass.greet = { "Hello, ${delegate.message}" } runTypeChecked(greeter) runCompileStatic(greeter) Hello, Good Morning Good Morning
  60. 60. @Transactional class MyService { @Transactional(propagation = Propagation.REQUIRES_NEW) def m1() {} def m2(){ m1() } }
  61. 61. Introduce Meta-programming
  62. 62. @Transactional class MyService implements ApplicationContextAware { ApplicationContext context @Transactional(propagation = Propagation.REQUIRES_NEW) def m1() {} def m2() { MyService service = context.getBean('myService') service.m1() } @Override void setApplicationContext(ApplicationContext applicationContext) throws BeansException { context = applicationContext } }
  63. 63. def grailsApplication def init = { servletContext -> injectSelfProxy() } private def injectSelfProxy(){ for (sc in grailsApplication.serviceClasses) { String propertyName = sc.propertyName sc.clazz.metaClass.getMyProxy = { -> grailsApplication.mainContext .getBean(propertyName) } } }
  64. 64. @Transactional class MyService { ApplicationContext context @Transactional(propagation = Propagation.REQUIRES_NEW) def m1() {} def m2() { myProxy.m1() } }
  65. 65. Introduce Traits
  66. 66. class Product { UUID uuid = UUID.randomUUID() //other fields } class Order { UUID uuid = UUID.randomUUID() //other fields }
  67. 67. class Product { UUID uuid = UUID.randomUUID() //other fields } class Order { UUID uuid = UUID.randomUUID() //other fields } trait BusinessObject { }
  68. 68. class Product implements BusinessObject { //other fields } class Order implements BusinessObject { //other fields } trait BusinessObject { UUID uuid = UUID.randomUUID() }
  69. 69. class Technologies extends ArrayList { def filterGr8() { this.findAll { it.startsWith('Gr')} } } Technologies list = [‘Groovy', 'Grails', 'Gradle', 'Java'] as Technologies println list println list.filterGr8()
  70. 70. Replace Inheritance with Delegation
  71. 71. class Technologies { @Delegate private List list Technologies(List list) { this.list = list } def filterGr8() { list.findAll { it.startsWith('Gr') } } }
  72. 72. Thank You

×