Diese Präsentation wurde erfolgreich gemeldet.
Wir verwenden Ihre LinkedIn Profilangaben und Informationen zu Ihren Aktivitäten, um Anzeigen zu personalisieren und Ihnen relevantere Inhalte anzuzeigen. Sie können Ihre Anzeigeneinstellungen jederzeit ändern.

Refactoring to Immutability

1.251 Aufrufe

Veröffentlicht am

Keynote presented at NewCrafts (2018-06-18)
Video available at https://vimeo.com/276832516

It has been said that immutability changes everything. But what does that mean in practice? What does it mean for existing code that looks more like the mutant apocalypse than an elegant application of mathematical thinking? Immutability can be an ideal that is hard to reach. Refactoring, on the other hand, is all about the art of the possible. In this talk we'll be clarifying motivation and exploring some approaches to help reducing state mutability in code.

Veröffentlicht in: Software

Refactoring to Immutability

  1. 1. Refactoring to Immutability @KevlinHenney
  2. 2. Functional programming
  3. 3. Functional programming typically avoids using mutable state. https://wiki.haskell.org/Functional_programming
  4. 4. https://xkcd.com/1270/ Functional programming combines the flexibility and power of abstract mathematics with the intuitive clarity of abstract mathematics.
  5. 5. Rien n'est plus dangereux qu'une idée, quand on n'a qu'une idée. Émile-Auguste Chartier
  6. 6. Nothing is more dangerous than an idea, when you have only one idea. Émile-Auguste Chartier
  7. 7. String
  8. 8. Asking a question should not change the answer.
  9. 9. To keep our C++ API boundary simple, we [...] adopted one-way data flow. The API consists of methods to perform fire-and-forget mutations and methods to compute view models required by specific views. To keep the code understandable, we write functional style code converting raw data objects into immutable view models by default. As we identified performance bottlenecks through profiling, we added caches to avoid recomputing unchanged intermediate results. The resulting functional code is easy to maintain, without sacrificing performance. https://code.facebook.com/posts/498597036962415/under-the-hood-building-moments/
  10. 10. Excel is the world's most popular functional language Simon Peyton-Jones
  11. 11. I want code to be reasonable
  12. 12. As a programmer I want code to be reasonable
  13. 13. reasonable
  14. 14. raisonnable
  15. 15. raison
  16. 16. raisonner
  17. 17. As a programmer I want code to be reasonable so that I can reason about it
  18. 18. A large fraction of the flaws in software development are due to programmers not fully understanding all the possible states their code may execute in. In a multithreaded environment, the lack of understanding and the resulting problems are greatly amplified, almost to the point of panic if you are paying attention. John Carmack http://www.gamasutra.com/view/news/169296/Indepth_Functional_programming_in_C.php
  19. 19. Mutable Immutable Unshared Shared Unshared mutable data needs no synchronisation Unshared immutable data needs no synchronisation Shared mutable data needs synchronisation Shared immutable data needs no synchronisation
  20. 20. Mutable Immutable Unshared Shared Unshared mutable data needs no synchronisation Unshared immutable data needs no synchronisation Shared mutable data needs synchronisation Shared immutable data needs no synchronisation The Synchronisation Quadrant
  21. 21. Architecture represents the significant design decisions that shape a system, where significant is measured by cost of change. Grady Booch
  22. 22. µονόλιθος
  23. 23. There is a beautiful angel in that block of marble, and I am going to find it. All I have to do is to knock off the outside pieces of marble, and be very careful not to cut into the angel with my chisel. George F Pentecost "The Angel in the Marble"
  24. 24. Functional Operational Developmental
  25. 25. Functional Operational Developmental
  26. 26. Nothing is more dangerous than an idea, when you have only one idea. Émile-Auguste Chartier
  27. 27. Nothing is more dangerous than OO, when you have only one object.
  28. 28. public class Clock { public static Clock Instance => ... public TimeOfDay Now => ... ... }
  29. 29. public void Example() { var now = Clock.Instance.Now; ... }
  30. 30. public void Example(Clock clock) { var now = clock.Now; ... }
  31. 31. public interface Clock { TimeOfDay Now => ... } public class ClockImpl : Clock { public static Clock Instance => ... ... }
  32. 32. public interface IClock { TimeOfDay Now => ... } public class Clock : IClock { public static IClock Instance => ... ... }
  33. 33. public interface IClock { TimeOfDay Now => ... } public class LocalClock : IClock { public static IClock Instance => ... ... }
  34. 34. public interface Clock { TimeOfDay Now => ... } public class LocalClock : Clock { public static Clock Instance => ... ... }
  35. 35. public void Example(Func<TimeOfDay> timeOfDay) { var now = timeOfDay(); ... }
  36. 36. public void Example(TimeOfDay now) { ... }
  37. 37. public class TimeOfDay { ... public int Hour { get ... set ... } public int Minute { get ... set ... } public void NextHour() ... public void NextMinute() ... }
  38. 38. public class TimeOfDay { private int minutes; public const int minutesInHour = 60; public const int hoursInDay = 24; public const int minutesInDay = hoursInDay * minutesInHour; private static int Wrap(int minutes) { return minutes % minutesInDay; } public int Hour { get { return minutes / minutesInHour; } set { if (value < 0 || value >= hoursInDay) throw new ArgumentException(); minutes = Wrap(value * minutesInHour + Minute); } } public int Minute { get { return minutes % minutesInHour; } set { if (value < 0 || value >= minutesInHour) throw new ArgumentException(); minutes = Wrap(Hour * minutesInHour + value); } } public void NextHour() { minutes = Wrap(minutes + minutesInHour); } public void NextMinute() { minutes = Wrap(minutes + 1); } }
  39. 39. public class TimeOfDay { ... public int Hour { get ... set ... } public int Minute { get ... set ... } public void NextHour() ... public void NextMinute() ... }
  40. 40. public class TimeOfDay { ... public int Hour { get ... set ... } public int Minute { get ... set ... } }
  41. 41. public class TimeOfDay { ... public int Hour { get ... } public int Minute { get ... } }
  42. 42. public class TimeOfDay { ... public int Hour => ... public int Minute => ... }
  43. 43. public class TimeOfDay { ... public TimeOfDay(int hour, int minute) ... public int Hour => ... public int Minute => ... }
  44. 44. public class TimeOfDay { ... public TimeOfDay(int hour, int minute) ... public int Hour => ... public int Minute => ... public TimeOfDay WithHour(int newHour) ... public TimeOfDay WithMinute(int newMinute) ... }
  45. 45. public class TimeOfDay { ... public TimeOfDay(int hour, int minute) ... public int Hour => ... public int Minute => ... public TimeOfDay WithHour(int newHour) ... public TimeOfDay WithMinute(int newMinute) ... public TimeOfDay NextHour() ... public TimeOfDay NextMinute() ... }
  46. 46. public class TimeOfDay { ... public TimeOfDay(int hour, int minute) ... public int Hour => ... public int Minute => ... public TimeOfDay WithHour(int newHour) ... public TimeOfDay WithMinute(int newMinute) ... public TimeOfDay NextHour() ... public TimeOfDay NextMinute() ... public class Builder { ... } }
  47. 47. public class TimeOfDay { private readonly int minutes; public const int minutesInHour = 60; public const int hoursInDay = 24; public const int minutesInDay = hoursInDay * minutesInHour; private TimeOfDay(int minutes) { this.minutes = minutes % minutesInDay; } public TimeOfDay(int hour, int minute) { if (hour < 0 || hour >= hoursInDay || minute < 0 || minute >= minutesInHour) throw new ArgumentException(); minutes = hour * minutesInHour + minute; } public int Hour => minutes / minutesInHour; public int Minute => minutes % minutesInHour; public TimeOfDay WithHour(int newHour) => new TimeOfDay(newHour, Minute); public TimeOfDay WithMinute(int newMinute) => new TimeOfDay(Hour, newMinute); public TimeOfDay NextHour() => new TimeOfDay(minutes + minutesInHour); public TimeOfDay NextMinute() => new TimeOfDay(minutes + 1); ... }
  48. 48. try { Integer.parseInt(time.substring(0, 2)); } catch (Exception x) { return false; } if (Integer.parseInt(time.substring(0, 2)) > 12) { return false; } ... if (!time.substring(9, 11).equals("AM") & !time.substring(9, 11).equals("PM")) { return false; } Burk Hufnagel "Put the Mouse Down and Step Away from the Keyboard"
  49. 49. Burk Hufnagel "Put the Mouse Down and Step Away from the Keyboard" return time.matches("(0[1-9]|1[0-2]):[0-5][0-9]:[0-5][0-9] ([AP]M)");
  50. 50. Je n'ai fait celle-ci plus longue que parce que je n'ai pas eu le loisir de la faire plus courte. Blaise Pascal
  51. 51. I have made this [letter] longer than usual because I have not had time to make it shorter. Blaise Pascal
  52. 52. // Get the unique surnames in uppercase of the // first 15 book authors that are 50 years old // or older? library.stream() .map(book -> book.getAuthor()) .filter(author -> author.getAge() >= 50) .limit(15) .map(Author::getSurname) .map(String::toUpperCase) .distinct() .collect(toList())
  53. 53. // Get the first 15 unique surnames in // uppercase of the book authors that are 50 // years old or older. library.stream() .map(book -> book.getAuthor()) .filter(author -> author.getAge() >= 50) .map(Author::getSurname) .map(String::toUpperCase) .distinct() .limit(15) .collect(toList())
  54. 54. // Get the unique surnames in uppercase of the // first 15 book authors that are 50 years old // or older. library.stream() .map(book -> book.getAuthor()) .filter(author -> author.getAge() >= 50) .distinct() .limit(15) .map(Author::getSurname) .map(String::toUpperCase) .distinct() .collect(toList())
  55. 55. // Get the unique surnames in uppercase of the // first 15 book authors that are 50 years old // or older. library.stream() .map(book -> book.getAuthor()) .distinct() .filter(author -> author.getAge() >= 50) .limit(15) .map(Author::getSurname) .map(String::toUpperCase) .distinct() .collect(toList())
  56. 56. // Get the unique surnames in uppercase of the // first 15 book authors that are 50 years old // or older? List<Author> authors = new ArrayList<Author>(); for (Book book : library) { Author author = book.getAuthor(); if (author.getAge() >= 50) { authors.add(author); if (authors.size() == 15) break; }
  57. 57. // Get the unique surnames in uppercase of the // first 15 book authors that are 50 years old // or older? List<Author> authors = new ArrayList<Author>(); for (Book book : library) { Author author = book.getAuthor(); if (author.getAge() >= 50) { authors.add(author); if (authors.size() == 15) break; } } List<String> result = new ArrayList<String>(); for(Author author : authors) { String name = author.getSurname().toUpperCase(); if (!result.contains(name)) result.add(name); }
  58. 58. // Get the first 15 unique surnames in // uppercase of the book authors that are 50 // years old or older. List<String> result = new ArrayList<String>(); for (Book book : library) { Author author = book.getAuthor(); if (author.getAge() >= 50) { String name = author.getSurname().toUpperCase(); if (!result.contains(name)) { result.add(name); if (result.size() == 15) break; } } }
  59. 59. // Get the unique surnames in uppercase of the // first 15 book authors that are 50 years old // or older. List<Author> authors = new ArrayList<Author>(); for (Book book : library) { Author author = book.getAuthor(); if (author.getAge() >= 50 && !authors.contains(author)) { authors.add(author); if (authors.size() == 15) break; } } List<String> result = new ArrayList<String>(); for(Author author : authors) { String name = author.getSurname().toUpperCase(); if (!result.contains(name)) result.add(name); }
  60. 60. Try to leave out the part that readers tend to skip. Elmore Leonard
  61. 61. When it is not necessary to change, it is necessary not to change. Lucius Cary

×