$30 off During Our Annual Pro Sale. View Details »

Interception - Droidcon NYC 2023

Sam Edwards
September 14, 2023

Interception - Droidcon NYC 2023

Embrace the Interceptor pattern of software design to create cleaner abstractions, enable flexibility, and set yourself up for writing high quality tests. This versatile approach empowers engineers with unmatched control, adaptability, and seamless integration with dependency injection. Join me to explore how interfaces and protocols serve as the foundation for making code flexible, adaptable and composable. We’ll talk about high level design playbooks as well as real-world examples you can start using today to leverage this software design pattern. Examples such as utilizing reflection-based Proxy Objects, employing "Fakes" in unit tests, wrapping 3rd party libraries and modifying network traffic.

Example Code on Github: https://github.com/handstandsam/interception-examples

Sam Edwards

September 14, 2023
Tweet

More Decks by Sam Edwards

Other Decks in Programming

Transcript

  1. Sam Edwards - @HandstandSam - Square
    Interception
    Droidcon NYC
    September 14, 2023
    #DCNYC23

    View Slide

  2. A
    Call
    B
    Completion
    Invocation

    View Slide

  3. A
    Call Chain
    B

    View Slide

  4. A
    Call Chain
    B

    View Slide

  5. Call Chain
    A B

    View Slide

  6. Call Chain with Interceptors
    A B

    View Slide

  7. Telephone Game ☎
    A B

    View Slide

  8. Interceptor Pattern 💻

    View Slide

  9. https://en.wikipedia.org/wiki/Interceptor_pattern
    Offer a way to change, or augment the


    usual processing cycle.
    Interceptor Pattern

    View Slide

  10. https://en.wikipedia.org/wiki/Interceptor_pattern
    The rest of the system does not have to


    know something has been added or


    changed and can keep working as before.
    Interceptor Pattern

    View Slide

  11. Call Chain with Interceptors
    A B

    View Slide

  12. Categories of Interception
    • Passthrough (NoOp)

    • Logging (Inspect)

    • Modifying/Transform (Change)

    • Circuit Breaker (Stop)

    View Slide

  13. OkHttp Interceptors

    View Slide

  14. OkHttp Interceptor Examples

    View Slide

  15. Adding Interceptors for OkHttp
    val okHttpClient
    =
    OkHttpClient.Builder()


    .build()

    View Slide

  16. Adding Interceptors for OkHttp
    val okHttpClient
    =
    OkHttpClient.Builder()


    .addInterceptor(MyInterceptor)


    .build()

    View Slide

  17. Adding Interceptors for OkHttp
    val okHttpClient
    =
    OkHttpClient.Builder()


    .addInterceptor(MyInterceptor)


    .addInterceptor(MyInterceptor2
    )

    .build()

    View Slide

  18. Adding Interceptors for OkHttp
    val okHttpClient
    =
    OkHttpClient.Builder()


    .addInterceptor(MyInterceptor)


    .addInterceptor(MyInterceptor2
    )

    .addInterceptor(MyInterceptor3
    )

    .build()

    View Slide

  19. Passthrough Interceptor
    🤷

    View Slide

  20. Passthrough Interceptor
    Interceptor { chain
    ->


    val request = chain.request()


    val response = chain.proceed(request)


    response


    }

    View Slide

  21. Logging Interceptor
    📝

    View Slide

  22. Logging Interceptor
    Interceptor { chain
    ->


    val request = chain.request()


    val response = chain.proceed(request)


    println("
    - - -
    Request Headers ---")


    println(request.headers)


    println("
    - - -
    Response Headers ---")


    println(response.headers)


    response


    }

    View Slide

  23. Modifying Interceptor
    🥷

    View Slide

  24. Modifying Interceptor
    Interceptor { chain
    ->


    val newRequest = chain.request()


    .newBuilder()


    .addHeader("Droidcon", "NYC")


    .build()


    chain.proceed(newRequest)


    }

    View Slide

  25. Short Circuit Interceptor

    View Slide

  26. Short Circuit Interceptor
    val ShortCircuitInterceptor
    =
    Interceptor { chain
    ->


    val request = chain.request()


    Response.Builder()


    .code(200)


    .protocol(Protocol.HTTP_2
    )

    .request(request)


    .message("")


    .body("Short Circuited".toResponseBody())


    .build()


    }

    View Slide

  27. Caching Interceptor
    🧠

    View Slide

  28. Caching Interceptor
    private val cache = mutableMapOf> ( )

    Interceptor { chain
    ->


    val request = chain.request()


    val bodyStr = cache[request.url] ?
    :
    run {


    val response = chain.proceed(request)


    response.body?.string().also {


    cache[request.url] = it


    }


    }


    buildResponseWithBodyStr(request, bodyStr)


    }

    View Slide

  29. OkHttp Interceptors are Beautiful 🌈
    • Composable

    • Flexible

    • Powerful

    View Slide

  30. Pop Quiz! 🏈

    View Slide

  31. American Football Interception 🏈

    View Slide

  32. When the ball is caught by the


    defense, instead of the offense.
    🏈

    View Slide

  33. A
    Interception
    American Football
    B

    View Slide

  34. A
    Interception
    American Football
    B
    🏈

    View Slide

  35. Interception
    American Football
    🏈 B
    A

    View Slide

  36. Recap

    View Slide

  37. Interceptor Pattern Benefits
    • Clean Abstraction

    • Flexible

    • Testable

    • Adaptable

    • Composable

    • Injectable

    View Slide

  38. Kotlin Flows

    View Slide

  39. Chaining Flows and Intercepting
    flowOf(1, 2, 3
    )

    .collect()

    View Slide

  40. Chaining Flows and Intercepting
    flowOf(1, 2, 3
    )

    .onEach { println("Logged $it") }


    .collect()
    Logged 1


    Logged 2


    Logged 3

    View Slide

  41. Chaining Flows and Intercepting
    flowOf(1, 2, 3
    )

    .onEach { println("Logged $it") }


    .map { it
    +
    100
    }

    .collect()
    Logged 1


    Logged 2


    Logged 3

    View Slide

  42. Chaining Flows and Intercepting
    flowOf(1, 2, 3
    )

    .onEach { println("Logged $it") }


    .map { it
    +
    100
    }

    .onEach { println("Collected $it") }


    .collect()
    Logged 1


    Collected 101


    Logged 2


    Collected 102


    Logged 3


    Collected 103

    View Slide

  43. Kotlin Flows are Beautiful 🌈
    • Composable

    • Flexible

    • Powerful

    View Slide

  44. Iterating Kotlin Collections

    View Slide

  45. Chaining Actions and Intercepting
    listOf(1, 2, 3
    )

    .onEach { println("Logged $it") }
    Logged 1


    Logged 2


    Logged 3

    View Slide

  46. Chaining Actions and Intercepting
    listOf(1, 2, 3
    )

    .onEach { println("Logged $it") }


    .map { it
    +
    100
    }
    Logged 1


    Logged 2


    Logged 3

    View Slide

  47. Chaining Actions and Intercepting
    listOf(1, 2, 3
    )

    .onEach { println("Logged $it") }


    .map { it
    +
    100
    }

    .onEach { println("Collected $it") }
    Logged 1


    Logged 2


    Logged 3


    Collected 101


    Collected 102


    Collected 103

    View Slide

  48. Interfaces Enable Injection

    View Slide

  49. Interfaces Enable Injection
    interface Logger {
    fun log(message: String)
    }

    View Slide

  50. Interfaces Enable Injection
    interface Logger {
    fun log(message: String)
    }
    class SystemOutLogger : Logger {
    override fun log(message: String) {
    println(message)
    }
    }

    View Slide

  51. Interfaces Enable Injection
    interface Logger {
    fun log(message: String)
    }
    class SystemOutLogger : Logger {
    override fun log(message: String) {
    println(message)
    }
    }
    class AndroidLogger : Logger {
    override fun log(message: String) {
    Log.d("Logger", message)
    }
    }

    View Slide

  52. Interfaces Enable Interception

    View Slide

  53. A
    Interfaces Enable Interception
    B
    SystemOutLogger
    Logger

    View Slide

  54. A
    Interfaces Enable Interception
    B
    Logger SystemOutLogger
    Logger

    View Slide

  55. A
    Interfaces Enable Interception
    B
    Logger SystemOutLogger
    Logger Logger

    View Slide

  56. Interfaces Enable Interception
    A B
    Logger SystemOutLogger
    Logger Logger Logger

    View Slide

  57. Interfaces Enable Fakes

    View Slide

  58. A
    Interfaces Enable Fakes
    B
    Interface 3rd Party Lib
    Fake

    View Slide

  59. Kotlin Delegates

    View Slide

  60. Kotlin Delegates
    Passthrough
    class PassthroughDelegateLogger(


    private val delegate: Logger


    )
    :
    Logger {


    override fun log(message: String) {


    delegate.log(message)


    }


    override fun error(message: String) {


    delegate.error(message)


    }


    }

    View Slide

  61. Kotlin Delegates
    Logging
    class LoggingDelegateLogger(


    private val delegate: Logger


    )
    :
    Logger by delegate {


    override fun log(message: String) {


    println("Logged $message")


    delegate.log(message)


    }


    }

    View Slide

  62. Kotlin Delegates
    Modifying
    class AddTimestampLogger(


    private val delegate: Logger


    )
    :
    Logger by delegate {


    override fun log(message: String) {


    delegate.log("${Date()} $message")


    }


    }

    View Slide

  63. Kotlin Delegates
    Short Circuit
    class ShortCircuitNoErrorsDelegateLogger(


    private val delegate: Logger


    )
    :
    Logger by delegate {


    override fun log(message: String) {


    delegate.log("${Date()} $message")


    }


    override fun error(message: String) {


    // Do Not Delegate


    }


    }

    View Slide

  64. Interception Recap

    View Slide

  65. Categories of Interception
    • Passthrough (NoOp)

    • Logging (Inspect)

    • Modifying/Transform (Change)

    • Circuit Breaker (Stop)

    View Slide

  66. Why?
    • Clean Abstraction

    • Flexible

    • Testable

    • Adaptable

    • Composable

    • Injectable

    View Slide

  67. Takeaways
    • Use Interfaces

    • OkHttp “Interception”

    • Kotlin Flow “Interception”

    • Kotlin Delegate “Interception”

    • Design for “Interception”

    View Slide

  68. BONUS LIVE DEMO


    Java Reflection Dynamic Proxies

    View Slide

  69. Sam Edwards - @HandstandSam - Square #DCNYC23
    conferenceChain.proceed(talk)

    View Slide