Advertisement

The Art of Clean code

Independent Trainer & Coach (victorrentea.ro); Technical Team Lead and Sr. Java Developer; JavaEE Architect. PhD(CS) at IBM Global Business Services
May. 30, 2017
Advertisement

More Related Content

Advertisement
Advertisement

The Art of Clean code

  1. VictorRentea.ro Enterprise Java Training VictorRentea.ro victor.rentea@gmail.com @victorrentea © Copyright Victor Rentea 2017 Victor Rentea 30 may 2017, Sofia The Art of Clean Code
  2. VictorRentea.ro2 Introduction Why ?! Names The power invested in you Functions SRP Classes The OOP utopia Comments Incompetence Clean Lambdas Handling a lightsaber Agenda
  3. VictorRentea.ro Spring, JavaEE, JPA/Hibernate Clean Code, Architectures Design Patterns TDD, Coach, Coding Dojos Java Performance, more... Victor Rentea Consultant, Technical Lead Lead Architect for major IBM client 3 Independent Trainer Clean Code Evangelist Speaker victor.rentea@gmail.com VictorRentea.ro@victorrentea 1000+ devs. 1500+ hrs. 5 yrs.
  4. VictorRentea.ro …does one thing well …reads like well written prose ...was written by someone who cared ...when each method you read turns out to be pretty much what you expected Anyone can write code that a computer understands, but few programmers know how to write Introduction Clean Code … 4 Bjarne Stroustrup inventor of C++ Grady Booch inventor of UML Michael Feathers Working Effectively with Legacy Code Martin Fowler author of Refactoring Ward Cunningham inventor of Wiki, eXtreme Programmingpretty much what you expected code that a human can understand
  5. VictorRentea.ro5 pretty much what you expected The Principle of Least Astonishment The code is wtf/min code quality meter
  6. VictorRentea.ro True cost of software = its maintenance Why so much !?! Cause we get slower, as the code degrades Introduction Why Clean Code ? 6 80% of the total
  7. VictorRentea.ro We READ 10x more time than we WRITE  Make it more readable, even if it’s harder to write Boy scout rule Always check in cleaner code than you found 7 Why Clean Code ?
  8. VictorRentea.ro8
  9. VictorRentea.ro9 Today, I stopped refactoring. Today, my application became Legacy.
  10. VictorRentea.ro11 Introduction Names The power that YOU have Functions Classes Formatting & Comments Clean Lambdas Agenda
  11. VictorRentea.ro Names 12 With Great Power Comes Great Responsibility - SpiderMan
  12. VictorRentea.ro Functions are verbs product(), transaction() searchProduct(), sendTransaction() Boolean names should answer yes/no isGoldClient(), areHostsValid() Names 13 “Well Written Prose” green 17 “Mike” ?! ?! ?!
  13. VictorRentea.ro Classes are nouns Customer, OrderDetails, OrderFacade Avoid meaningless names OrderInfo, OrderData vs Order Delete the interfaces ICustomerService, OrderServiceImpl Names 14 “Well Written Prose” Delete the interfaces OrderServiceImpl
  14. VictorRentea.ro15 Delete the interfaces except: Remoting/API your-app-api.jar Strategy Pattern® Select implementation dynamically at runtime Dependency Inversion Implemented in lower-level modules But that’s another talk 
  15. VictorRentea.ro - You read the /* dusty old comment */ ? - You hunt down the code calling it ? - You decrypt its implementation ? Names How do you understand a function ? 16 You should NOT need to do that! Make the name speak for itself !
  16. VictorRentea.ro You think you found a better name? 17 Make the name speak for itself ! It takes seconds with an IDE (and rarely fails: XMLs, reflection,…) Yes, you did! Rename it ! OMG! It’s impossible! I CAN’T be smarter than The Elders!!
  17. VictorRentea.ro Names 18 Continuous Renaming There are no perfect names ! The team will be grateful! Yourself will be grateful, in 3 months! There are only two things hard in programming: Cache Invalidation and Naming Things (as you learn the application) Rename it !
  18. VictorRentea.ro19 Continuous Renaming
  19. VictorRentea.ro Pronounceable int getinvcdtlmt() int getInvoiceableCreditLimit() Avoid abbreviations ! Unless it’s a basic business concept, like “VAT” Names: Several Guidelines Names should be … 20
  20. VictorRentea.ro Consistent .find…() .fetch…() or .get…() ? Stick to naming conventions Unique Synonyms confuse. Don't use buyer or client to refer to a customer 21 Names should be …
  21. VictorRentea.ro Business-IT Gap 22
  22. VictorRentea.ro26 Introduction Names Functions Single Responsibility Principle Classes Formatting & Comments Clean Lambdas Agenda
  23. VictorRentea.ro A function should do one thing, it should do it well, and it should do it ONLY Functions 27 Small They should be Uncle Bob
  24. VictorRentea.ro wtf/min How small ? Functions Functions Should be Small !! 28 5 lines(by any means, smaller than a page of your IDE !) To be sure that they do just 1 THING Now really, what CAN you do in 5 lines ?? So you CAN find a good name for it Small Why so small ?!!
  25. VictorRentea.ro The function is a landscape Easy to remember by its author But for the team, it's like wilderness Functions 29 else for if if for if i fif for if if forif you don’t…
  26. VictorRentea.ro30 doOtherStuff(); if (cr323) { } …, boolean cr323) What do you do ? EXTRACT METHODS Change Request #323
  27. VictorRentea.ro Functions Why do small functions scare us? 31 Performance ? NO! Smaller methods run faster ! (get faster) Just google “Just-In-Time Compiler Optimizations”
  28. VictorRentea.ro32 Measure, Don’t Guess® Performance ? Premature optimization is the root of all evil – Donald Knuth – Kirk Pepperdine
  29. VictorRentea.ro Instead of a familiar landscape, I’m now juggling with tons of small functions I can’t even recall all their names but, The Team will thank you ! Even you will be grateful, in 3 months :) 33 else for if Why do small functions scare us?
  30. VictorRentea.ro No boolean params 37 removeOrders(customer, false, true); No nullable params => 4 (usually 3) if (customer != null) {…} else {…} = laziness/fear/rush = legacy => 2 What about invalid data?
  31. VictorRentea.ro Avoid returning null Wrap it in an Optional<> Throw exception Functions 38 Queue DB NULL WARS Defensive Programming Thoroughly check data only at the boundaries exception (corrupt data) Web Service File
  32. VictorRentea.ro try throws catch (Exception t) {/*Luv U!*/} We Runtime Exceptions (‘cause we don’t see them) 39 exceptionUnchecked Exceptions won the War ! catch (MyTimeoutException e) {retry();} new MyException(ErrorCode.USER_EXISTS); try {…} catch (Exception e) { log(e);} My preferences: Global exception handler Nice (i18n) user messages: enum New Exception only for selective catch
  33. VictorRentea.ro Continuous Refactoring You are not done when the code starts working! It is then when you should Clean Up your code !! ( IDEs help you a lot ) How to write such Functions ? 41 all tests pass !
  34. VictorRentea.ro42 public List<Integer> stringsToInts(List<String> strings) { if (strings != null) { List<Integer> integers = new ArrayList<>(); for (String s : strings) { integers.add(Integer.parseInt(s)); } return integers; } else { return null; } } How to simplify it ? List<Integer> integers = new ArrayList<>(); for (String s : strings) { integers.add(Integer.parseInt(s)); } return integers; An else follows…
  35. VictorRentea.ro43 public List<Integer> stringsToInts(List<String> strings) { if (strings == null) { return null; } List<Integer> integers = new ArrayList<>(); for (String s : strings) { integers.add(Integer.parseInt(s)); } return integers; } List<Integer> integers = new ArrayList<>(); for (String s : strings) { integers.add(Integer.parseInt(s)); } return integers; Less TABs => easier to read Fail the build if you detect 3+ TABs in a new method Early Return
  36. VictorRentea.ro How to refactor God Methods of 100-1500 lines ? Extract Method Object refactor 44 Hint A class should have less than 200-300 lines You make it a class
  37. VictorRentea.ro Extract Method Object refactor 45 public String doComplexStuff(String text) { Set<String> words=new HashSet<>(); int totalWords = 0; int totalPhrases = 0; StringTokenizer tokenizer = … while (tokenizer.hasMoreTokens()) { String next = tokenizer.nextToken(); … if (…) { for (…) {…} … } else { … } } } // line 534  public class DoComplexStuffCommand { private final String text; private Set<String> words=new HashSet<>(); private int totalWords = 0; private int totalPhrases = 0; private StringTokenizer tokenizer; public ExtractedMethodCommand(String t) { this.text = t; tokenizer=new StringTokenizer(text," "); } public String execute() {…} } Extracting a method may require many parameters, output parameters or be even impossible These methods use instance fields, and take no/fewer arguments String result = doComplexStuff(”bla”); = new DoComplexStuffCommand(”bla”).execute(); STATEFUL
  38. VictorRentea.ro46 Introduction Names Functions Classes The OOP utopia Formatting & Comments Clean Lambdas Agenda
  39. VictorRentea.ro47 struct(remember the C language ?)
  40. VictorRentea.ro =classes that expose all their state Data structures 48 public class PhoneBookEntryDTO { private String firstName; private String lastName; private String phoneNumber; public String getFirstName() { return firstName; } public void setFirstName(String first) { this.firstName = first; } public String getLastName() { … } public void setLastName(…) { … } public String getPhoneNumber() { … } public void setPhoneNumber(…) { … } } public class SimpleImmutableStruct { private final String firstName; private final String lastName; public SimpleImmutableStruct( String first, String last) { this.firstName = first; this.lastName = last; } public String getFirstName() { return firstName; } public String getLastName() { return lastName; } } We Immutable Objects: - If created valid, remain so - Thread safe - Safe to put in Tree*/Hash* public class PhoneBookEntryDTO { public String firstName; public String lastName; public String phoneNumber; } ≈ struct
  41. VictorRentea.ro49 OOP
  42. VictorRentea.ro Expose Behavior, not data Data is an implementation detail: it WILL change OOP 50 car.engineStarted=true car.setEngineStarted(true) car.startEngine() Information Hiding Tell as little as possible car.getGasolineInLiters() car.getPercentageFuelLeft() car.getEstimatedRemainingKm() Allowing the implementation to evolve without breaking your clients
  43. VictorRentea.ro51 But at work we don’t do OOP, do we ? In our Enterprise App, we write procedural code
  44. VictorRentea.ro52 What’s the goal of an Enterprise Application ? Existing procedures. (usually) To automate an existing business process. Procedural Code
  45. VictorRentea.ro53 Lots Procedural Code and lots and lots and lots and lots of it… How do we organize it, to Keep It Short & Simple ?
  46. VictorRentea.ro54 Procedural Code We distribute logic in many classes: Keep It Short & Simple Classes as containers of logic (in enterprise applications) !!
  47. VictorRentea.ro55 So, Do we ever do OOP? Yes! When writing Libraries and (mini) Frameworks Versioning Hell mycorp-commons- .jar
  48. VictorRentea.ro63 Introduction Names Functions Classes Formatting & Comments Clean Lambdas Agenda
  49. VictorRentea.ro64
  50. VictorRentea.ro65
  51. VictorRentea.ro66 At work, it’s not Matrix, you know… It’s Team Work
  52. VictorRentea.ro67 Don’t Code! Communicate !
  53. VictorRentea.ro Respect your readers Details matter: 10x more reading, remember ? Write Literature Always the simplest code that works. Never obfuscate Don’t Code! Communicate !
  54. VictorRentea.ro70 Static imports assertEquals(), when(), toList(), emptyList(),… Tune your IDE Default code blocks catch(Exception e) { throw new RuntimeException(e); } Learn those shortcuts ! Super-Fast Deploy Hot bytecode replace ? etc...
  55. VictorRentea.ro public class SomeClass { … public void someMethod(…) { … } … } Formatting Suggestions… 71 { } 100-200 lines never > 500 5-10 lines never >1 screen 100-120 chars 2-3-4 chars / TAB? Spaces, code style, … Team ?
  56. VictorRentea.ro Written proof of incompetence Always before dropping a comment, and Try to express it in code Why do we hate comments ? Comments LIE ! They inevitably fall out of sync with the code /* very important comment from 10 years ago that tells that... */ Comments = Failures 72
  57. VictorRentea.ro Readable Constants - Instead of magic numbers Explanatory Variables - Name intermediary results - Break multiline && conditions || formulas Explanatory Methods - "Encapsulate Conditionals" Split your methods - into many small functions with long descriptive names 73 public List<int[]> getCells() { List<int[]> list1 = new ArrayList<int[]>(); for (int[] x : theList) if (x[0] == 4) list1.add(x); return list1; } public List<int[]> getFlaggedCells(){ List<int[]> flaggedCells = new A…L…<…>(); for (int[] cell : gameBoard) { boolean isFlagged= cell[STATUS]==FLAGGED; if (isFlagged) flaggedCells.add(x); } return flaggedCells; } public List<Cell> getFlaggedCells(){ List<Cell> flaggedCells = new A…L…<Cell>(); for (Cell cell : gameBoard) { if (cell.isFlagged()) flaggedCells.add(cell); } return flaggedCells; }
  58. VictorRentea.ro Mumbling - Unclear! - Rush/lazy? - Forgotten? Redundant Position Markers //////////////// Accessors //////////////// Commented-out Code Don't read it. Just DELETE IT! Git rocks! Non-Local Comment on that same line !! (±1) Over-involvement Wiki article syndrome Bad Comments 74 /** Returns the day of the month. * @return the day of the month. */ public int getDayOfMonth() { return dayOfMonth; } DUH !! stream.close(); } catch (IOException e) { // Give me a break! } /** Default constructor. */
  59. VictorRentea.ro Intent: WHY?(what?) When the code just can't say it specific algorithms: http://wiki... bug workarounds: http://jira... Clarifications to explain calls to a strange API Warning of consequences TODOs // TODO vrentea Fetch the order Assume ownership Prepare for D-day Public API JavaDocs For libraries/frameworks you write Legal // Copyright (C) 2013, … Good Comments 75 …=new SimpleDateFormat(…); // Not thread-safe
  60. VictorRentea.ro76 Introduction Names Functions Classes Formatting & Comments Clean Lambdas Agenda
  61. VictorRentea.ro Clean Lambdas Why Lambdas ? 77 They are cool
  62. VictorRentea.ro78 But is the Code Cleaner ? (than in Java7)
  63. VictorRentea.ro Clean Lambdas 79 .filter(MyObj::isActive) Expressive ? or Cryptic ? .filter(order -> order.getOrderLines().stream() .filter(line->line.isActive()).findFirst().isPresent()) .map(Order::getOrderLines) .flatMap(List::stream) .filter(orderLine -> orderLine.getCount().compareTo(new BigDecimal(“2”)) .collect(groupingBy(OrderLine::getProduct, groupingBy(line->line.getOrder().getPaymentMethod(), summingInt(OrderLine::getCount); My own little Clean Lambdas study
  64. VictorRentea.ro80 .filter(order -> order.getOrderLines().stream() .filter(line->line.isActive()).findFirst().isPresent()) .map(Order::getOrderLines) .flatMap(List::stream) .filter(orderLine -> orderLine.getCount().compareTo(new BigDecimal(“2”)) .collect(groupingBy(OrderLine::getProduct, groupingBy(line->line.getOrder().getPaymentMethod(), summingInt(OrderLine::getCount); Avoid breaking lines Myownlittle CleanLambdasstudy http://bit.ly/2dFf2fi
  65. VictorRentea.rohttp://bit.ly/2dFf2fi Clean Lambdas Encapsulate Predicates 81 Set<Customer> customersToNotify = orders.stream() .filter(order -> order.getDeliveryDueDate().before(warningDate) && order.getOrderLines().stream() .anyMatch(line -> line.getStatus() != Status.IN_STOCK)) .map(Order::getCustomer) .collect(toSet());
  66. VictorRentea.ro Local Variables 87 Predicate<Order> needsTracking = order -> order.getPrice() > 1000; Set<Customer> customersToNotify = orders.stream() .filter(deliveryDueBefore(warningDate).or(needsTracking)) .filter(this::hasLinesNotInStock) .map(Order::getCustomer) .collect(toSet()); class OrderLine { ... public boolean isNotInStock() { return status != Status.IN_STOCK; } } public boolean hasLinesNotInStock(Order order) { return order.getOrderLines().stream() .anyMatch(OrderLine::isNotInStock); } public static Predicate<Order> deliveryDueBefore(Date date) { return order -> order.getDeliveryDueDate().before(date); } http://bit.ly/2dFf2fi Helper methods in this:: Helper getters in entities Functions returning Predicates
  67. VictorRentea.ro88 Clean Code in Java8 Try some lib? if checked Ex, >2 params jOOL, vavr,… For more, check-out the screen cast of my Clean Lambdas workshop at Voxxed Bucharest 2017, on my website 
  68. VictorRentea.ro Pair Programming 89 Peer Review!
  69. VictorRentea.ro90 Peer Review! Pair Programming
  70. VictorRentea.ro93 Introduction Names Functions Classes Formatting & Comments Clean Lambdas Agenda
  71. VictorRentea.ro Key Points 94 Introduction Names Functions Classes Formatting & Comments Clean Lambdas Agenda Refine Expressive Names Stop Refactor = Start Legacy Short methods Structs, Objects or Logic Containers ? Comments are Failures. Expressive code. Pair Programming is the way
  72. 98 How to apply all this in my legacy code ?? Where can I read more ? LET’S PRACTICE !!! ©
  73. VictorRentea.ro99  http://literatejava.com/exception s/checked-exceptions-javas- biggest-mistake/  http://stackoverflow.com/questio ns/613954/the-case-against- checked-exceptions  7 Virtutes of a Good Object  https://dzone.com/articles/the- worst-mistake-of-computer- science-1  http://blog.8thlight.com/uncle- bob/2012/08/13/the-clean- architecture.html  http://commadot.com/wtf-per- minute/  SOLID is WRONG: https://speakerdeck.com/tastapo d/why-every-element-of-solid-is- wrong  Depenency Injection leads to Clean Code : https://youtu.be/RlfLCWKxHJ0 Check out my tweets for more
  74. VictorRentea.ro Enterprise Java Training VictorRentea.ro victor.rentea@gmail.com @victorrentea © Copyright Victor Rentea 2017 Victor Rentea 30 may 2017, Sofia The Art of Clean Code Special Thanks to JPoint Conference Team, Ionuţ Chereji, Florin Diaconu Victor Bucuţea, Ionuţ Scutaru Laurenţiu Carată, Leonard Giura
Advertisement