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

Clean Code

Avatar for GDP Labs GDP Labs
March 24, 2017

Clean Code

Avatar for GDP Labs

GDP Labs

March 24, 2017
Tweet

More Decks by GDP Labs

Other Decks in Programming

Transcript

  1. 4 Issues • Bad Code • Great vs Bad Company

    • Who is Responsible? ! © GDP Labs 2017
  2. 5 • Technical debt: the eventual consequences of poor software

    architecture and software development within a code base. ◦ Rushed the product to the market ◦ Had made a huge mess in the code ◦ Added more features ◦ The code got worse and worse Bad Code © GDP Labs 2017
  3. 6 © GDP Labs 2017 • LeBlanc’s Law: Later equals

    never ◦ They never go back to fix the bad code • Sink projects, careers and companies • Example: netscape and myspace Bad Code
  4. 11 • BOD? • Management? • Program Manager? • Product

    Manager? • Unrealistic schedule? Who is Responsible for Bad Code? Unprofessional Software Engineers. It is your job to defend your code with equal passion. © GDP Labs 2017
  5. For the things we have to learn before we can

    do them, we learn by doing them - Aristotle 13
  6. 14 Solution • Naming • Function • Comment • Formatting

    • Objects and Data Structures • Error Handling • Class • Emergence © GDP Labs 2017
  7. 15 Naming The name of a variable, function, or class,

    should answer all the big questions. It should tell you why it exists, what it does, and how it is used. - Robert C. Martin ” “ © GDP Labs 2017
  8. 16 public List<int[]> getThem() { List<int[]> list1 = new ArrayList<int[]>();

    for (int[] x : theList) if (x[0] == 4) list1.add(x); return list1; } Say that we’re working in a mine sweeper game. Naming © GDP Labs 2017
  9. 17 Use intention-revealing names public List<int[]> getFlaggedCells() { List<int[]> flaggedCells

    = new ArrayList<int[]>(); for (int[] cell : gameBoard) if (cell[STATUS_VALUE] == FLAGGED) flaggedCells.add(cell); return flaggedCells; } Naming © GDP Labs 2017
  10. 18 Use intention-revealing names public List<Cell> getFlaggedCells() { List<Cell> flaggedCells

    = new ArrayList<Cell>(); for (Cell cell : gameBoard) if (cell.isFlagged()) flaggedCells.add(cell); return flaggedCells; } Naming © GDP Labs 2017
  11. 19 Avoid misleading and unmeaningful distinctions int a = l;

    if (O == l) a = O1; else l = 01; private void copyChars(char a1[], char a2[]) { for (int i = 0; i < a1.length; i++) { a2[i] = a1[i]; } } Naming © GDP Labs 2017 MISLEADING UNMEANINGFUL DISTINCTIONS
  12. 20 public class DtaRcrd102 { private Date genymdhms; private Date

    modymdhms; private final String pszqint = ”102”; /* … */ } CAN YOU PRONOUNCE THEM? Naming © GDP Labs 2017
  13. 21 Use pronounceable names public class Customer { private Date

    generationTimestamp; private Date modificationTimestamp; private final String recordId = ”102”; /* … */ } Naming © GDP Labs 2017
  14. 22 Classes and objects should be noun, methods should be

    verb public class Point { private int x, y; public Point(int x, int y) {...} public int getX() {...} public void setX(int x) {...} public int getY() {...} public void setY(int y) {...} } Naming © GDP Labs 2017
  15. 23 public class Circle { double radius; String color; public

    double getRadius() {...} public String fetchColor() {...} public double retrieveArea() {...} } GET OR FETCH OR RETRIEVE? Naming © GDP Labs 2017
  16. 24 Pick one word per concept public class Circle {

    double radius; String color; public double getRadius() {...} public String getColor() {...} public double getArea() {...} } Naming © GDP Labs 2017
  17. 25 for (int i=0; i<34; i++) { s += (t[i]*4)/5;

    } if single-letter names and numeric constants are used across body of text, they are not easy to locate NOT EASY TO SEARCH Naming © GDP Labs 2017
  18. 26 Use searchable names int realDaysPerIdealDay = 4; const int

    WORK_DAYS_PER_WEEK = 5; int sum = 0; for (int i=0; i < NUMBER_OF_TASKS; i++) { int realTaskDays = taskEstimate[i] * realDaysPerIdealDay; int realTaskWeeks = (realdays / WORK_DAYS_PER_WEEK); sum += realTaskWeeks; } Naming © GDP Labs 2017
  19. 27 Function The first rule of functions is that they

    should be small. The second rule of functions is that they should be smaller than that. - Robert C. Martin ” “ © GDP Labs 2017
  20. 28 public static String renderPageWithSetupsAndTeardowns( PageData pageData, boolean isSuite )

    throws Exception { boolean isTestPage = pageData.hasAttribute("Test"); if (isTestPage) { WikiPage testPage = pageData.getWikiPage(); StringBuffer newPageContent = new StringBuffer(); includeSetupPages(testPage, newPageContent, isSuite); newPageContent.append(pageData.getContent()); includeTeardownPages(testPage, newPageContent, isSuite); pageData.setContent(newPageContent.toString()); } return pageData.getHtml(); } TOO BIG
  21. 29 Do one thing public static String renderPageWithSetupsAndTeardowns( PageData pageData,

    boolean isSuite) throws Exception { if (isTestPage(pageData)) includeSetupAndTeardownPages(pageData, isSuite); return pageData.getHtml(); } Function is doing more than “one thing” if you can extract another function from it with a name that is not merely a restatement Function © GDP Labs 2017
  22. 30 public boolean set(String attribute, String value); if (set(”username”, ”unclebob”))

    Is it asking whether the “username” attribute was previously set to “unclebob”? Or is it asking whether the “username” attribute was successfully set to “unclebob”? Function © GDP Labs 2017
  23. 31 Do something or answer something, but not both if

    (attributeExists(”username”)) { setAttribute(”username”, ”unclebob”); } Function © GDP Labs 2017
  24. 32 Transformational function should appear as the return value StringBuffer

    transform(StringBuffer in) void transform(StringBuffer out) These implementation simply returns the input argument Function © GDP Labs 2017
  25. 33 Avoid too many function parameters Car createCar(Wheel wheel, Engine

    engine); Car createCar(float wheelDiameter, float wheelColor, float wheelMaterial, float wheelManufacturer, String engineType, String engineColor, String engineManufacturer); Too many parameters will make function hard to be understood and maintained. Function © GDP Labs 2017
  26. 34 Comments Good code is its own best documentation. As

    you’re about to add a comment, ask yourself, ‘How can I improve the code so that this comment isn’t needed?' - Steve McConnell ” “ © GDP Labs 2017
  27. // Throws an exception // if the timeout is reached.

    public synchronized void waitForClose( final long timeoutMillis) throws Exception { if(!closed) { wait(timeoutMillis); if(!closed) throw new Exception( "MockResponseSender could not be closed"); } } 35 Comments © GDP Labs 2017 Redundant Comments The comment is not more informative than the code
  28. /** * Returns the day of the month. * @return

    the day of the month. */ public int getDayOfMonth() { return dayOfMonth; } 36 Comments © GDP Labs 2017 Noise Comments Only restate the obvious and provide no new information
  29. Explain yourself in code 37 // Check to see if

    the employee is eligible for full benefits if ((employee.flags & HOURLY_FLAG) && (employee.age > 65)) if (employee.isEligibleForFullBenefits()) Comments © GDP Labs 2017
  30. // format matched kk:mm:ss EEE, MMM dd, yyyy Pattern timeMatcher

    = Pattern.compile("\\d*:\\d*:\\d* \\w*, \\w* \\d*, \\d*"); INFORMATIVE public void writeJournal(Diary diary) { for (Thread thread : threadList) { thread.run(() -> { // Prevent original object to be modified Diary copyOfDiary = new Diary(diary); write(copyOfDiary); }); } } EXPLANATION OF INTENT 38 Comments © GDP Labs 2017
  31. public static SimpleDateFormat makeStandardHttpDateFormat() { //SimpleDateFormat is not thread safe,

    //so we need to create each instance independently. SimpleDateFormat df = new SimpleDateFormat( ”EEE, dd MMM yyyy HH:mm:ss z”); df.setTimeZone(TimeZone.getTimeZone(”GMT”)); return df; } WARNING OF CONSEQUENCES 39 Comments © GDP Labs 2017
  32. Variables Should be declared as close to their usage as

    possible 41 public void paySalary(Employee employee) { float bonus; float totalSalary; bonus = calculateBonus(employee.getSalary()); totalSalary = bonus + employee.getSalary(); sendMoney(employee, totalSalary); } Formatting © GDP Labs 2017
  33. 42 Variables Should be declared as close to their usage

    as possible Formatting public void paySalary(Employee employee) { float bonus; bonus = calculateBonus(employee.getSalary()); float totalSalary; totalSalary = bonus + employee.getSalary(); sendMoney(employee, totalSalary); } © GDP Labs 2017
  34. 43 public class Employee { private String name; private String

    id; private float salary; public void getName() { … } } Instance Variables Should be declared at the top of the class Formatting © GDP Labs 2017
  35. 44 public void paySalary() { calculateBonus(salary); } private float calculateBonus(float

    salary) { return (salary / 10); } Dependent Functions Should be vertically close, and the caller should be above the called Formatting © GDP Labs 2017
  36. 45 public class Employee { public void payTax() { }

    public void payOverdueTax(Date date) { } public void increaseSalary() { } public void decreaseSalary() { } } Conceptual Affinity Certain bits of code want to be near other bits Formatting © GDP Labs 2017
  37. public float volume (float length,float width,float height) { // code

    } 46 public float volume(float length, float width, float height) { // code } Space Formatting © GDP Labs 2017
  38. 47 public class WebService { private Request request; private Response

    response; private FitnesseContext context; protected long requestTimeLimit; } Horizontal Alignment Formatting © GDP Labs 2017 DIFFICULT TO MAINTAIN
  39. public class WebService { private Request request; private Response response;

    private FitnesseContext context; protected long requestTimeLimit; } 48 Horizontal Alignment Formatting © GDP Labs 2017
  40. 51 public class Point { public double x; public double

    y; } Data structures expose data and have no behavior Data structures make it easy to add functions without the need to modify existing structures. Objects and Data Structures © GDP Labs 2017
  41. 52 public class Vehicle { public getFuelCapacity() {...} public getPercentageFuelRemaining()

    {...} } Object expose behavior and hide data Objects make it easy to add classes without the need to modify existing functions. Objects and Data Structures © GDP Labs 2017
  42. 53 Law of Demeter Given method f of class C,

    f should only call methods of: • C • An Object created by f • An Object passed as an argument to f • An instance variable of C Objects and Data Structures © GDP Labs 2017
  43. 54 public class Vehicle { public getFuelCapacity() {...} public getPercentageFuelRemaining()

    { return (fuel / getFuelCapacity() * 100); } } Law of Demeter Given method f of class C, f should only call methods of: • C Objects and Data Structures © GDP Labs 2017
  44. 55 public void registryEmployee() { Employee newEmployee = new Employee();

    registry.createEmplyeeId(newEmployee); employeeIdList.add(newEmployee.getId()); } Law of Demeter Given method f of class C, f should only call methods of: • An Object created by f Objects and Data Structures © GDP Labs 2017
  45. 56 public void calculateEmployeeBonus(Employee employee) { return (employee.getSalary() / 10);

    } Law of Demeter Given method f of class C, f should only call methods of: • An Object passed as an argument to f Objects and Data Structures © GDP Labs 2017
  46. 57 public class Car { private Engine engine; public String

    getCarFuelType() { return engine.getFuelType(); } } Law of Demeter Given method f of class C, f should only call methods of: • An instance variable of C Objects and Data Structures © GDP Labs 2017
  47. if (deletePage(page) == E_OK) { if (registry.delete(page.reference) == E_OK) {

    logger.log(“page deleted”); } else { logger.log(“delete registry failed”); } } else { logger.log(“delete failed”); } 59 Prefer exceptions to return error code Error Handling © GDP Labs 2017
  48. try { deletePage(page); registry.delete(page.reference); } catch (Exception e) { logger.log(e.getMessage());

    } 60 Prefer exceptions to return error code Error Handling © GDP Labs 2017
  49. • Functions should do one thing and error handling is

    one thing ◦ Implement the normal flow of the function • Don’t return NULL ◦ So other function doesn’t need to implement error handling • Don’t pass NULL ◦ So other function doesn’t need to implement error handling 61 Error Handling © GDP Labs 2017
  50. 63 public class TimeCalculator { public static final int TIME

    = 25; // public static constant public static int DURATION = 25; // private static variables private int now; // private instance variables public int addTime(int time) { // public functions … configTime = getConfigTime() … } private int getConfigTime() { … } // private utilities } Class Class Organization Declare the constants, variables, and methods in this order: © GDP Labs 2017
  51. Classes should be small! • The first rule is that

    they should be small • The second rule is that they should be smaller than that Single Responsibility Principle (SRP) • A class or module should have one, and only one, reason to change • SRP is one of the more important concept in OO design 64 Class © GDP Labs 2017
  52. Refactored: 65 public class TimeCalculator { … public int addTime(int

    time) { … } … } public class UserConfiguration { … public int getConfigTime() { … } … } Class © GDP Labs 2017
  53. Cohesion • Classes should have a small number of instance

    variables • The more variables (or class modules) a method manipulates the more cohesive that method is to its class. • A class in which each variable is used by each method is maximally cohesive. • Maintaining cohesion results in many small classes 66 Class © GDP Labs 2017
  54. 67 public class CustomStack { private int topOfStack = 0;

    private int duration = 100; List<Integer> elements = new LinkedList<Integer>(); public int size() { … } public void push(int element) { … } public int pop() throws PoppedWhenEmpty { … } public void sleep() { TimeUnit.SECONDS.sleep(duration); } public void log() { … logger.log(Level.WARNING, ”This is a warning!”); … } … } LOW COHESION © GDP Labs 2017
  55. 68 public class Stack { private int topOfStack = 0;

    List<Integer> elements = new LinkedList<Integer>(); public int size() { return topOfStack; } public void push(int element) { topOfStack++; elements.add(element); } public int pop() throws PoppedWhenEmpty { if (topOfStack == 0) throw new PoppedWhenEmpty(); int element = elements.get(--topOfStack); elements.remove(topOfStack); return element; } } HIGH COHESION © GDP Labs 2017
  56. • Runs all the tests. To make it easy, make

    sure: ◦ Low coupling ◦ SRP • Contains no duplication (Refactoring) • Expresses the intent of the programmer (Refactoring) ◦ Easy to read and understand • Minimizes the number of classes and methods (Refactoring) ◦ But don’t take it too far 70 Emergence © GDP Labs 2017
  57. 71 public void scaleToOneDimension(…) { … RenderedOp newImage = ImageUtilities.getScaledImage(

    image, scalingFactor, scalingFactor); image.dispose(); System.gc(); image = newImage; } public synchronized void rotate(int degrees) { RenderedOp newImage = ImageUtilities.getRotatedImage( image, degrees); image.dispose(); System.gc(); image = newImage; } DUPLICATION © GDP Labs 2017
  58. 72 public void scaleToOneDimension(…) { … replaceImage(ImageUtilities.getScaledImage( image, scalingFactor, scalingFactor));

    } public synchronized void rotate(int degrees) { replaceImage(ImageUtilities.getRotatedImage( image, degrees)); } private void replaceImage(RenderedOp newImage) { image.dispose(); System.gc(); image = newImage; } DUPLICATION REFACTORED © GDP Labs 2017
  59. Give a man a fish and you feed him for

    a day; teach a man to fish and you feed him for a lifetime. - Anne Thackeray Ritchie 73
  60. 74 Conclusion 1. Practice Is there a set of simple

    practices that can replace experience? Clearly not 2. Beware of Bad Code or Code Smell 3. Refactor © GDP Labs 2017
  61. Clean Code: A Handbook of Agile Software Craftsmanship Robert C.

    Martin ©2009 | Prentice Hall 75 © GDP Labs 2017
  62. A programmer who writes clean code is an artist who

    can take a blank screen through a series of transformations until it is an elegantly coded system 76
  63. Cracking the Coding Interview: 189 Programming Questions and Solutions Gayle

    Laakmann McDowell ©2015 | CareerCup 79 © GDP Labs 2017
  64. • Large class • Feature envy • Inappropriate intimacy •

    Refused bequest • Lazy class • Excessive use of literals • Cyclomatic complexity • Data clump • Orphan variable or constant class 81 • Duplicated code • Too many parameters • Long method • Excessive return of data • Excessively long identifiers • Excessively short identifiers Code Smells © GDP Labs 2017
  65. Single-responsibility principle Open-closed principle Liskov substitution principle Interface segregation principle

    Dependency Inversion Principle 82 OO Design Principles: S.O.L.I.D S O L I D © GDP Labs 2017