Clean Code with Kotlin

Clean Code with Kotlin

With Kotlin we are able to write concise, expressive and safe code. Sounds like clean code, doesn’t it? During this presentation we will recap what clean code is, we will highlight the importance of defining meaningful names and how to write clean functions and classes. Finally we will be able to learn more about the advantages of immutability and how to handle the errors in Kotlin. By the end of the session, you will have a better understanding of what clean code means and you will learn a series of tips and tricks ready to be applied in your code.

44a168e6578c2cc83aaf54a38458ade9?s=128

Magda Miu

June 10, 2020
Tweet

Transcript

  1. Magda Miu @magdamiu Squad Lead Developer at Orange Android Google

    Developer Expert Clean Code with Kotlin
  2. A. A big software system that works perfectly but is

    very difficult to change. What would you prefer to have: B. A big software system with some missing features that is very easy to change.
  3. None
  4. What is Clean Code? “Clean code is readable. It tells

    a story.” Uncle Bob, Clean Code
  5. None
  6. None
  7. Code quality “measure” Few WTFs Developer Many WTFs Developer WTF/min

    WTF = What a Terrible Feature
  8. MEANINGFUL NAMES

  9. A name should reveal intent and should answer at these

    questions: • Why it exists? • What it does? • How it is used? Use intention-revealing names
  10. Examples Types Names Classes and Objects Customer, Account, WikiPage Methods

    postPayment, deleteAccount, displayPage Solution domain names AccountVisitor Problem domain names churnPerMonth
  11. “UNCLEAN” CODE CLEAN CODE

  12. if (t % 5 == 0) { val w =

    t / 5 println("$w week(s)") } else { println("$t days") } TODO: display the number of days/weeks until an offer will expire
  13. if (daysUntilOfferEnds % numberOfDaysInAWeek == 0) { val numberOfWeeks =

    daysUntilOfferEnds / numberOfDaysInAWeek println("$numberOfWeeks week(s)") } else { println("$daysUntilOfferEnds days") } TODO: display the number of days/weeks until an offer will expire
  14. class Address { var addressCountry: String? = null var addressCity:

    String? = null var addressStreety: String? = null var addressStreetNo: Int = 0 } TODO: define a class for geo addresses
  15. class Address { var country: String? = null var city:

    String? = null var street: String? = null var streetNumber: Int = 0 } TODO: define a class for geo addresses
  16. users.filter{ it.job == Job.Developer } .map{ it.birthDate.dayOfMonth } .filter{ it

    <= 10 } .min() Warning: Use explicit argument names and avoid using too often “it”
  17. users.filter{ user -> user.job == Job.Developer } .map{ developer ->

    developer.birthDate.dayOfMonth } .filter { birthDay -> birthDay <= 10 } .min() Warning: Use explicit argument names and avoid using too often “it”
  18. FUNCTIONS

  19. Functions are the verbs of the language, and classes are

    the nouns.
  20. Uncle Bob Functions should do one thing. They should do

    it well. They should do it only.
  21. Basic Rules: ➔ The functions should be small ➔ The

    functions should be smaller than that Make it smaller
  22. Extra Rules: ➔ No many arguments ➔ No side effects

    ➔ Indent level max 2 Make it smaller
  23. fun parseProduct(response: Response?): Product? { if (response == null) {

    throw ClientException("Response is null") } val code: Int = response.code() if (code == 200 || code == 201) { return mapToDTO(response.body()) } if (code >= 400 && code <= 499) { throw ClientException("Invalid request") } if (code >= 500 && code <= 599) { throw ClientException("Server error") } throw ClientException("Error $code") }
  24. fun parseProduct(response: Response?) = when (response?.code()){ null -> throw ClientException("Response

    is null") 200, 201 -> mapToDTO(response.body()) in 400..499 -> throw ClientException("Invalid request") in 500..599 -> throw ClientException("Server error") else -> throw ClientException("Error ${response.code()}") }
  25. for (user in users) { if(user.subscriptions != null) { if

    (user.subscriptions.size > 0) { var isYoungerThan30 = user.isYoungerThan30() if (isYoungerThan30) { countUsers++ } } } } WARNING: keep the abstraction level consistent and avoid nested code
  26. var countUsersYoungerThan30WithSubscriptions = 0 for (user in users) { if

    (user.isYoungerThan30WithSubscriptions) { countUsersYoungerThan30WithSubscriptions++; } } WARNING: keep the abstraction level consistent and avoid nested code
  27. fun sumUpUserPoints(): Int { var sumAllPoints = 0 for (user

    in users) { sumAllPoints += user.points sendEmail(user) } return sumAllPoints } No side effects Side effect
  28. Command Query Separation Command Query Output System System System Changed

    state Output (result) Changed state Output (result)
  29. override fun onSaveInstanceState(outState: Bundle) { super.onSaveInstanceState(outState) outState.putString(KEY_USER_NAME, username) } Command

  30. fun getProfileInfo(account: SignInAccount): UserProfile { var userProfile = UserProfile() if

    (account != null) { userProfile.name = account.displayName userProfile.email = account.email userProfile.id = account.id } return userProfile } Query
  31. CLASSES Dog Cat Baby Dog Pet

  32. Class organization • A class should be smaller than small.

    • It should have only one responsibility and a single reason to change. • A class collaborates with a few others to achieve the desired system behaviors.
  33. The newspaper metaphor

  34. // var and val control the variable itself // not

    the object instance that is assigned val year = 2019 year = 2020 var age = 18 age = 19 Immutable References
  35. val listOfYears = listOf(2017, 2018, 2019, 2020) listOfYears.add(2021) Read-only collections

  36. data class Movie( val name: String, val studio: String, val

    rating: Float, val releaseYear: Int = 2020 ) Immutable Data Classes Constructor (assign args to props) Only getters toString() hashCode(), equals() copy() final Default arguments
  37. Cohesion • Cohesion is a measure of the degree to

    which the elements of the class/ module are functionally related. • When classes lose cohesion, split them.
  38. None
  39. Coupling • Coupling is the measure of the degree of

    interdependence between the modules. • This isolation makes it easier to understand each element of the system.
  40. None
  41. Best case scenario Cohesion Coupling

  42. Principles 1. DRY 2. KISS 3. YAGNI 4. SOLID

  43. • Don’t Repeat Yourself • Applicable whenever we copy /

    paste a piece of code D.R.Y.
  44. • Keep It Simple and Stupid • Whenever we want

    to implement a method to do all things K.I.S.S.
  45. • You Ain’t Gonna Need It • Don’t write code

    which is not yet necessary Y.A.G.N.I.
  46. • Single responsibility (SRP) • Open-closed (OCP) • Liskov substitution

    (LSP) • Interface segregation (ISP) • Dependency inversion (DIP) S.O.L.I.D.
  47. OBJECTS AND DATA STRUCTURES

  48. OBJECTS DATA STRUCTURES ➔ hide their data behind abstractions ➔

    expose functions to operate on their data ➔ expose their data ➔ have no meaningful functions Data/object anti-symmetry
  49. class Company { var name: String = "" var address:

    String = "" } DATA STRUCTURE
  50. class Company { var name: String = "" get() {

    if (!field.isEmpty()) { return field.toUpperCase() } return field } var address: String = "" } OBJECT
  51. Train wrecks • Chains of calls are generally considered to

    be sloppy style and should be avoided. • Object interactions spliced into each other can be confusing.
  52. fun getColorOfFolder(folder: Folder): String? { return folder.feature.tag.color.name }

  53. fun getColorOfFolder(folder: Folder): String? { return tagColorName(tagOfFeatureFolder(folder)) } fun getTagColorName(tag:

    Tag): String? { return tag.color.name } fun getTagOfFeatureFolder(folder: Folder): Tag { return folder.feature.tag }
  54. The law of Demeter A module should not know about

    the internals of the objects it interacts with. Demeter Goddess of the Harvest
  55. The law of Demeter Only talk to your friends who

    share your concerns Demeter Goddess of the Harvest
  56. A method f of a class C should only call

    the methods of these: 1. C 2. An object created by f 3. An object passed as an argument to f 4. An object held in an instance variable of C LoD - Formal definition
  57. Law of Demeter A B B is a friend of

    A C C is a friend of B Note: A friend of a friend is a stranger Messages from A to B are OK Messages from A to C are discouraged
  58. fun displayInfoAboutOwner(car: Car) { var city = car.owner.address.city println("${car.owner.name} lives

    in $city") } Car Owner Owner is a friend of Car Address Address is a friend of Owner
  59. App “architecture” Model Room Remote Data source Retrofit SQLite REST

    API Activity / Fragment
  60. Recommended app architecture Activity / Fragment Model Room Remote Data

    source Retrofit SQLite REST API ViewModel LiveData Repository
  61. Room.databaseBuilder(context.getApplicationContext(), UserDatabase::class.java, DATABASE_NAME) .addMigrations(MIGRATION_1_2, MIGRATION_2_3) .fallbackToDestructiveMigrationFrom(4) .build() Fluent APIs

  62. “Tell, Don't Ask” Principle (4) • Object-orientation is about bundling

    data with the functions that operate on that data. • Rather than asking an object for data and acting on that data, we should instead tell an object what to do.
  63. class Balance(var value: Double) class Client { fun alertService(balances: List<Balance>)

    { for (balance in balances) { if (balance.value == 0.0) { // alert } else { // everything is ok } } } }
  64. class Balance(var value: Double, val alertThreshold: Int) { val isExceedsThreshold:

    Boolean get() = value >= alertThreshold }
  65. class Client { fun alertService(balances: List<Balance>) { for (balance in

    balances) { if (balance.isExceedsThreshold) { // alert } else { // everything is ok } } } }
  66. Objects vs Data Tell, don’t ask. Don’t talk to strangers.

    Dealing with Objects? Dealing with Data?
  67. ERROR HANDLING

  68. • Prefer exceptions to returning error codes. • Error handling

    is important, but if it obscures logic, it’s wrong. • In Kotlin we have only unchecked exceptions. • Define dedicated exception classes. • Don’t return or pass null. Error handling
  69. Types/ Nullable Types (recap) @Nullable Type @NotNull Type Java Type

    Type? Type Kotlin ?
  70. Nullability in Kotlin String? “Clean Code” null String “Clean Code”

  71. val name: String = if (nullableValue == null) "default" else

    nullableValue Smart-cast
  72. val view = activity!!.findViewById<View>(R.id.menu_add) kotlin.KotlinNullPointerException Not null assertion operator !!

    OR Bang! Bang! Boom! operator
  73. fun computeSqrt(number: Double): Double { if(number >= 0) { return

    Math.sqrt(number) } else { throw RuntimeException("No negative please") } } Nothing is something...
  74. fun getMovie(id: Int): Movie { val movie = movieRepository.findMovie(id) return

    movie ?: throw RuntimeException("Movie not found") } Throw exceptions
  75. sealed class MovieSearchResult data class MovieFound(val movie: Movie) : MovieSearchResult()

    object MovieNotFound : MovieSearchResult() object DatabaseOffline : MovieSearchResult() fun getMovie(id: Int): MovieSearchResult { val movie = movieRepository.findMovie(id) return if (movie == null) { MovieNotFound } else { MovieFound(movie) } } Return result class
  76. inputStream.use { outputStream.use { // do something with the streams

    outputStream.write(inputStream.read()) } } “try-with-resources” in Kotlin - initial solution
  77. arrayOf(inputStream, outputStream).use { // do something with the streams outputStream.write(inputStream.read())

    } “try-with-resources” in Kotlin - improved solution
  78. private inline fun <T : Closeable?> Array<T>.use(block: ()->Unit) { //

    implementation } use implementation
  79. CODE REVIEW

  80. CODE REVIEW

  81. 1. Help to improve the quality and consistency of the

    code 2. Exchange of knowledge and best practices 3. Learn the code base 4. New perspective(s) on your code 5. Learn new tips and tricks about writing code Code Review Advantages
  82. Reviewer Author <Code>

  83. Author

  84. • Make sure you understand your task • Refactor the

    code if it’s unreadable • Write tests and follow the team conventions • Format your code before commit it Writing the code
  85. The boy scout rule Leave the campground cleaner than you

    found it.
  86. • Add relevant commit comments • Send pull requests often

    • Have minimum 2 reviewers (one is senior) Before the code review
  87. • Be humble • You are on the same side

    with your reviewer(s) • Know when to unlearn the old habits After the code review
  88. Reviewer

  89. • I think… • I would… • I believe… •

    I suggest... Use I… comments
  90. • Have you consider using… ? • What do you

    think about… ? • Have you tried to… ? Ask questions
  91. • This code… • This function… • This line of

    code... It’s about the code, not about the coder
  92. Feedback equation* Observation of a behavior Impact of the behavior

    Question or Request I observed this function has 60 lines. This makes it difficult for me to understand the logic. I suggest extracting a part of the code into other functions and give them relevant names. * Defined by Lara Hogan
  93. 1. Define with your team a set of conventions 2.

    Justify technology use 3. Enforce good practices (XP) 4. Question until you understand 5. Criticize ideas, not people 6. Testing, testing, testing 7. Integrate early, integrate often 8. Emphasize collective ownership of code 9. Prioritize and actively evaluate trade-offs 10. Listen to users My summary
  94. Coding is not a sprint It’s a marathon

  95. CREDITS: This presentation template was created by Slidesgo, including icons

    by Flaticon, and infographics & images by Freepik THANKS Do you have any questions? @magdamiu
  96. 1. Article “CommandQuerySeparation” by Martin Fowler 2. “Law of Demeter:

    Principle of Least Knowledge” by Professor Karl, Northeastern University 3. Article “Breaking the Law of Demeter is Like Looking for a Needle in the Haystack” by Miško Hevery 4. Article “TellDontAsk” by Martin Fowler 5. Article “One Assertion Per Test” by Dave Astels 6. The Tragedy Of Checked Exceptions by Howard Lewis Ship 7. Exceptions are Bad by Jed Wesley-Smith 8. Checked exceptions I love you, but you have to go by Miško Hevery If you want to learn more...