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

Kotlin Everywhere Hamburg: Refactoring Legacy Code with Kotlin and Coroutines

Kotlin Everywhere Hamburg: Refactoring Legacy Code with Kotlin and Coroutines

Legacy code can be quite the challenge to manage, often resulting from untested scenarios, quick fixes, or less than successful initiatives. With few developers wanting to deal with it, it can end up with little remaining knowledge of it's inner workings.

We can take many learnings from Michael Feathers book on "Working Effectively with Legacy Code", but we can also use Kotlin migration as an effective tool to leverage the management, reduction, and removal of legacy code in our applications.

In this session, you'll learn how to get started with Kotlin in your projects, tips and tricks on how to preserve your version control history, some pitfalls when migrating from Java, and what new technologies you can make use of in your journey with Kotlin.

Ash Davies

August 30, 2019
Tweet

More Decks by Ash Davies

Other Decks in Programming

Transcript

  1. Refactoring Legacy Code with Kotlin & Coroutines
    Kotlin Everywhere: Hamburg
    @askashdavies

    View Slide

  2. @askashdavies

    View Slide

  3. Java
    @askashdavies

    View Slide

  4. { }
    @askashdavies

    View Slide

  5. Streams ↝
    @askashdavies

    View Slide

  6. @askashdavies

    View Slide

  7. Kotlin
    @askashdavies

    View Slide

  8. Null
    @askashdavies

    View Slide

  9. if (foo == null) {
    bar();
    }
    @askashdavies

    View Slide

  10. O"ensive Code < >
    @askashdavies

    View Slide

  11. Urgency
    @askashdavies

    View Slide

  12. Kotlin?
    @askashdavies

    View Slide

  13. Kotlin
    @askashdavies

    View Slide

  14. @askashdavies

    View Slide

  15. Google IO 2018
    @askashdavies

    View Slide

  16. ! "
    @askashdavies

    View Slide

  17. Google IO 2019
    @askashdavies

    View Slide

  18. Kotlin
    @askashdavies

    View Slide

  19. Libraries
    @askashdavies

    View Slide

  20. @askashdavies

    View Slide

  21. @askashdavies

    View Slide

  22. @askashdavies

    View Slide

  23. @askashdavies

    View Slide

  24. @askashdavies

    View Slide

  25. @askashdavies

    View Slide

  26. Kotlin
    @askashdavies

    View Slide

  27. Idiomacy
    @askashdavies

    View Slide

  28. @askashdavies

    View Slide

  29. Idiomatic Code
    @askashdavies

    View Slide

  30. Idiomatic Code
    • Consistent, easier to read
    • Less cognitive load
    • Less ambiguity
    • Function > Style
    @askashdavies

    View Slide

  31. Code Style
    • kotlinlang.org/docs/reference/coding-conventions.html
    • android.github.io/kotlin-guides/style.html
    @askashdavies

    View Slide

  32. Refactoring Legacy Code
    @askashdavies

    View Slide

  33. ⌃⌥⇧K
    @askashdavies

    View Slide

  34. @askashdavies

    View Slide

  35. @askashdavies

    View Slide

  36. public class BadJavaActivity extends Activity {
    @Inject Dependency dependency;
    }
    @askashdavies

    View Slide

  37. General Assumptions
    @askashdavies

    View Slide

  38. class BadKotlinActivity : Activity() {
    @Inject var dependency: Dependency? = null
    }
    @askashdavies

    View Slide

  39. class SlightlyLessBadKotlinActivity : Activity() {
    @Inject internal lateinit var dependency: Dependency
    }
    @askashdavies

    View Slide

  40. UninitializedPrope.yAccessException!
    @askashdavies

    View Slide

  41. lateinit var file: File
    if (::file.isInitialized) { /* ... */ }
    @askashdavies

    View Slide

  42. Data Classes
    @askashdavies

    View Slide

  43. public class User {
    private String firstName;
    private String lastName;
    public User(String firstName, String lastName) {
    this.firstName = firstName;
    this.lastName = lastName;
    }
    public String getFirstName() {
    return firstName;
    }
    public void setFirstName(String firstName) {
    this.firstName = firstName;
    }
    public String getLastName() {
    return lastName;
    }
    public void setLastName(String lastName) {
    this.lastName = lastName;
    }
    @Override
    public boolean equals(Object o) {
    if (this == o) {
    return true;
    }
    if (o == null || getClass() != o.getClass()) {
    return false;
    }
    User user = (User) o;
    return Objects.equals(firstName, user.firstName) &&
    Objects.equals(lastName, user.lastName);
    }
    @Override
    public int hashCode() {
    return Objects.hash(firstName, lastName);
    }
    }
    @askashdavies

    View Slide

  44. class User(var firstName: String?, var lastName: String?) {
    override fun equals(o: Any?): Boolean {
    if (this === o) {
    return true
    }
    if (o == null || javaClass != o.javaClass) {
    return false
    }
    val user = o as User?
    return firstName == user!!.firstName && lastName == user.lastName
    }
    override fun hashCode(): Int {
    return Objects.hash(firstName, lastName)
    }
    }
    @askashdavies

    View Slide

  45. data class User(val firstName: String, val lastName: String)
    @askashdavies

    View Slide

  46. data class User(val firstName: String, val lastName: String? = null)
    @askashdavies

    View Slide

  47. @NotNull
    public final User copy(@Nullable String firstName, @Nullable String lastName) {
    return new User(firstName, lastName);
    }
    @askashdavies

    View Slide

  48. Kotlin
    • Singleton objects
    • String interpolation
    • Elvis operator
    !
    • Destructuring
    • Extension functions
    • Scoping functions
    @askashdavies

    View Slide

  49. Maintaining History
    @askashdavies

    View Slide

  50. Maintaining History
    • Change extension .java -> .kt
    • First commit
    • Apply Kotlin conversion
    • Second commit
    @askashdavies

    View Slide

  51. Maintaining History
    Refactoring
    @askashdavies

    View Slide

  52. Refactoring
    @askashdavies

    View Slide

  53. Refactoring
    SOLID
    • Single-responsibility
    • Open-closed
    • Liskov substitution
    • Interface segregation
    • Dependency inversion
    @askashdavies

    View Slide

  54. Refactoring
    Single Responsibility
    @askashdavies

    View Slide

  55. Perspective
    @askashdavies

    View Slide

  56. Asynchronicity
    @askashdavies

    View Slide

  57. Concurrency is Hard
    @askashdavies

    View Slide

  58. Thread
    new Thread(() -> {
    foo();
    }).start();
    @askashdavies

    View Slide

  59. CompletableFuture
    CompletableFuture.supplyAsync(this::findSomeData)
    .thenApply(this:: intReturningMethod)
    .thenAccept(this::notify);
    @askashdavies

    View Slide

  60. RxJava
    Observable
    .fromIterable(resourceDraft.getResources())
    .flatMap(resourceServiceApiClient::createUploadContainer)
    .zipWith(Observable.fromIterable(resourceDraft.getResources()), Pair::create)
    .flatMap(uploadResources())
    .toList()
    .toObservable()
    .flatMapMaybe(resourceCache.getResourceCachedItem())
    .defaultIfEmpty(Resource.getDefaultItem())
    .flatMap(postResource(resourceId, resourceDraft.getText(), currentUser, resourceDraft.getIntent()))
    .observeOn(AndroidSchedulers.mainThread())
    .subscribeOn(Schedulers.io())
    .subscribe(
    resource -> repository.setResource(resourceId, resource, provisionalResourceId),
    resourceUploadError(resourceId, resourceDraft, provisionalResourceId)
    );
    @askashdavies

    View Slide

  61. @askashdavies

    View Slide

  62. What If?
    @askashdavies

    View Slide

  63. Coroutines
    @askashdavies

    View Slide

  64. fun main() {
    GlobalScope.launch {
    delay(1000L)
    println("World!")
    }
    println("Hello,")
    Thread.sleep(2000L)
    }
    // Hello,
    // World!
    @askashdavies

    View Slide

  65. fun main() {
    GlobalScope.launch {
    delay(1000L)
    println("World!")
    }
    println("Hello,")
    Thread.sleep(2000L)
    }
    // Hello,
    // World!
    @askashdavies

    View Slide

  66. fun main() {
    GlobalScope.launch {
    delay(1000L)
    println("World!")
    }
    println("Hello,")
    Thread.sleep(2000L)
    }
    // Hello,
    // World!
    @askashdavies

    View Slide


  67. Stability
    @askashdavies

    View Slide

  68. @askashdavies

    View Slide

  69. @Annotations
    (
    !
    Here be dragons)
    @askashdavies

    View Slide

  70. Annotations
    @ExperimentalCoroutinesApi //

    @askashdavies

    View Slide

  71. Annotations
    @ExperimentalCoroutinesApi //

    @FlowPreview //

    @askashdavies

    View Slide

  72. Annotations
    @ExperimentalCoroutinesApi //

    @FlowPreview //

    @ObsoleteCoroutinesApi //

    @askashdavies

    View Slide

  73. Annotations
    @ExperimentalCoroutinesApi //

    @FlowPreview //

    @ObsoleteCoroutinesApi //

    @InternalCoroutinesApi //

    @askashdavies

    View Slide

  74. Annotations
    @Experimental
    @askashdavies

    View Slide

  75. !
    Coroutines
    @askashdavies

    View Slide

  76. !
    Native (rst-pa-y library
    @askashdavies

    View Slide

  77. E"cient
    @askashdavies

    View Slide

  78. !
    Easy-to-use
    @askashdavies

    View Slide

  79. !
    suspend fun
    @askashdavies

    View Slide

  80. !
    Suspend
    fun main() {
    GlobalScope.launch {
    delay(1000L)
    println("World!")
    }
    println("Hello,")
    Thread.sleep(2000L)
    }
    // Hello,
    // World!
    @askashdavies

    View Slide

  81. !
    Suspend
    fun main() {
    GlobalScope.launch {
    doWorld()
    println("Hello,")
    Thread.sleep(2000L)
    }
    }
    suspend fun doWorld() {
    delay(1000L)
    println("World!")
    }
    // Hello,
    // World!
    @askashdavies

    View Slide

  82. !
    Suspend
    fun main() {
    GlobalScope.launch {
    doWorld()
    println("Hello,")
    Thread.sleep(2000L)
    }
    }
    suspend fun doWorld() {
    withContext(Dispatchers.IO) {
    delay(1000L)
    println("World!")
    }
    }
    // Hello,
    // World!
    @askashdavies

    View Slide

  83. Dispatchers
    @askashdavies

    View Slide

  84. Dispatchers
    • Default
    • IO
    • Main
    • Android (Main Thread Dispatcher)
    • JavaFx (Application Thread Dispatcher)
    • Swing (Event Dispatcher Thread)
    • Unconfined
    @askashdavies

    View Slide

  85. Testing
    @Test
    fun testFoo() = runBlockingTest {
    val actual = foo()
    // ...
    }
    suspend fun foo() {
    delay(1_000)
    // ...
    }
    @askashdavies

    View Slide

  86. Fu#her Reading
    • Google Codelab: Refactoring to Kotlin
    https://codelabs.developers.google.com/codelabs/java-to-kotlin/
    • KotlinX Coroutine Test
    https://github.com/Kotlin/kotlinx.coroutines/tree/master/kotlinx-coroutines-test
    • Sean McQuillan: Coroutines + Testing =
    https://www.droidcon.com/media-detail?video=352671106
    • Ash Davies: RxJava & Coroutines: A Practical Analysis v3
    https://speakerdeck.com/ashdavies/rxjava-and-coroutines-a-practical-analysis-
    v3
    @askashdavies

    View Slide

  87. Thanks!
    @askashdavies

    View Slide