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

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. 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
  2. Categories of Interception • Passthrough (NoOp) • Logging (Inspect) •

    Modifying/Transform (Change) • Circuit Breaker (Stop)
  3. Passthrough Interceptor Interceptor { chain -> val request = chain.request()

    val response = chain.proceed(request) response }
  4. 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 }
  5. Modifying Interceptor Interceptor { chain -> val newRequest = chain.request()

    .newBuilder() .addHeader("Droidcon", "NYC") .build() chain.proceed(newRequest) }
  6. 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() }
  7. Caching Interceptor private val cache = mutableMapOf<HttpUrl, String? > (

    ) 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) }
  8. Chaining Flows and Intercepting flowOf(1, 2, 3 ) .onEach {

    println("Logged $it") } .collect() Logged 1 Logged 2 Logged 3
  9. Chaining Flows and Intercepting flowOf(1, 2, 3 ) .onEach {

    println("Logged $it") } .map { it + 100 } .collect() Logged 1 Logged 2 Logged 3
  10. 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
  11. Chaining Actions and Intercepting listOf(1, 2, 3 ) .onEach {

    println("Logged $it") } Logged 1 Logged 2 Logged 3
  12. Chaining Actions and Intercepting listOf(1, 2, 3 ) .onEach {

    println("Logged $it") } .map { it + 100 } Logged 1 Logged 2 Logged 3
  13. 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
  14. Interfaces Enable Injection interface Logger { fun log(message: String) }

    class SystemOutLogger : Logger { override fun log(message: String) { println(message) } }
  15. 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) } }
  16. 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) } }
  17. Kotlin Delegates Logging class LoggingDelegateLogger( private val delegate: Logger )

    : Logger by delegate { override fun log(message: String) { println("Logged $message") delegate.log(message) } }
  18. Kotlin Delegates Modifying class AddTimestampLogger( private val delegate: Logger )

    : Logger by delegate { override fun log(message: String) { delegate.log("${Date()} $message") } }
  19. 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 } }
  20. Categories of Interception • Passthrough (NoOp) • Logging (Inspect) •

    Modifying/Transform (Change) • Circuit Breaker (Stop)
  21. Takeaways • Use Interfaces • OkHttp “Interception” • Kotlin Flow

    “Interception” • Kotlin Delegate “Interception” • Design for “Interception”