---
title: "Elegant Event Handling in Kotlin - A Refactoring Walkthrough"
description: "Unlock peak performance in Kotlin code with these expert refactoring tips."
url: "https://img.ly/blog/kotlin-code-refactoring-for-peak-performance/"
type: "blog"
date: "2023-09-29"
author: "Sven"
tags: ["How-To","Android","Android App Development","Kotlin","Insights"]
---

> This is the markdown version of [Elegant Event Handling in Kotlin - A Refactoring Walkthrough](https://img.ly/blog/kotlin-code-refactoring-for-peak-performance/). For all pages in one file, see [llms-full.txt](https://img.ly/llms-full.txt). For an index of all available pages, see [llms.txt](https://img.ly/llms.txt).

---

In the world of software development, code refactoring is the hero that rescues us from tangled and inefficient code. In this article, we'll embark on an adventure to revamp Kotlin code handling diverse events. Our mission? To enhance performance and style, making the code sleeker, more maintainable, and a joy to work with.

## What We Aim to Achieve

On this journey to transform Kotlin event handling, our goal is to refine our code to be more efficient, readable, and maintainable. We're introducing a variety of improvements, including:

- Replacing a convoluted `when` statement with a `HashMap` for lightning-fast (O(1)) performance.
- Infusing syntactic sweetness with inline functions and [reified](https://kotlinlang.org/docs/inline-functions.html#reified-type-parameters) type parameters.
- Employing delegated properties for cleaner dependency injection.
- Adhering to the Single Responsibility Principle by enabling multiple specialized event handler functions.

## Step 1: The Starting Line

![sven2244_a_playful_abstract_artwork_that_represents_the_complex_6e9491ff-8203-4ae2-9a5e-52025e4010be.png](https://blog.img.ly/2023/09/2.png)

Our adventure begins with a glance at the original code. This codebase manages a variety of block events through a function named `handleBlockEvent` and an event handler function called `onEvent`. Let's unveil the original code:

```kotlin
open fun onEvent(event: Event) {
    // ...
    handleBlockEvent(engine, getBlockForEvents(), checkNotNull(assetsRepo.fontFamilies.value).getOrThrow())
}

fun handleBlockEvent(engine: Engine, block: DesignBlock, fontFamilyMap: Map<String, FontFamilyData>, event: BlockEvent) {
    when (event) {
        BlockEvent.OnDelete -> engine.delete(block)
        BlockEvent.OnBackward -> engine.sendBackward(block)
        BlockEvent.OnDuplicate -> engine.duplicate(block)
        BlockEvent.OnForward -> engine.bringForward(block)
        BlockEvent.ToBack -> engine.sendToBack(block)
        BlockEvent.ToFront -> engine.bringToFront(block)
        BlockEvent.OnChangeFinish -> engine.editor.addUndoStep()
        is BlockEvent.OnChangeBlendMode -> onChangeBlendMode(engine, block, event.blendMode)
        is BlockEvent.OnChangeOpacity -> engine.block.setOpacity(block, event.opacity)
        is BlockEvent.OnChangeFillColor -> onChangeFillColor(engine, block, event.color)
        // and so on...
    }
}

sealed class BlockEvent : Event {
    object OnChangeFinish : BlockEvent
    object OnForward : BlockEvent
    object OnBackward : BlockEvent
    object OnDuplicate : BlockEvent
    object OnDelete : BlockEvent
    object ToFront : BlockEvent
    object ToBack : BlockEvent
    data class OnChangeBlendMode(val blendMode: BlendMode) : BlockEvent
    data class OnChangeOpacity(val opacity: Float) : BlockEvent
    data class OnChangeFillColor(val color: Color) : BlockEvent
    // and so on...
}
```

To use the original code, you'd typically call the `onEvent` function with a specific event:

```kotlin
onEvent(BlockEvent.OnChangeFillColor(Color.RED))
```

This would then trigger the `handleBlockEvent` function to deal with the event at hand. Now, let's embark on our first refactoring adventure.

## Step 2: Unveiling HashMaps and Payloads for Peak Performance

![sven2244_an_abstract_image_ofa_futuristic_cityscape_with_neon-l_fbda2c5b-b80f-430b-9f00-609ceecc1292.png](https://blog.img.ly/2023/09/3.png)

In our first act of refactoring, we introduce a trusty `HashMap` to map each event type to its corresponding action. This heroic move eliminates the need for the convoluted `when` statement, making our code more efficient. We also unveil a payload mechanism to convey essential data to the event handlers.

Behold the refactored code:

```kotlin
abstract class EventsHandler<Payloads>(
    val fillPayload: (cache: Payloads) -> Unit
) {
    abstract val payloadCache: Payloads
    private val eventMap = mutableMapOf<KClass<out Event>, Payloads.(event: Event) -> Unit>()

    fun handleEvent(event: Event) {
        eventMap[event::class]?.let {
            it.invoke(payloadCache.also { fillPayload(it) }, event)
        }
    }

    operator fun <EventType : Event> set(event: KClass<out EventType>, lambda: Payloads.(event: EventType) -> Unit) {
        eventMap[event] = lambda as Payloads.(event: Event) -> Unit
    }
}

class BlockEventsHandler(fillPayload: (cache: BlockEventsHandler.Payloads) -> Unit) : EventsHandler<BlockEventsHandler.Payloads>(fillPayload) {
    class Payloads {
        lateinit var engine: Engine
        lateinit var block: DesignBlock
        lateinit var fontFamilyMap: Map<String, FontFamilyData>
    }
    override val payloadCache: Payloads = Payloads()

    init {
        it[BlockEvent.OnDelete::class] = { engine.delete(block) }
        it[BlockEvent.OnBackward::class] = { engine.sendBackward(block) }
        it[BlockEvent.OnDuplicate::class] = { engine.duplicate(block) }
        it[BlockEvent.OnForward::class] = { engine.bringForward(block) }
        it[BlockEvent.ToBack::class] = { engine.sendToBack(block) }
        it[BlockEvent.ToFront::class] = { engine.bringToFront(block) }
        it[BlockEvent.OnChangeFinish::class] = { engine.editor.addUndoStep() }
        it[BlockEvent.OnChangeBlendMode::class] = { onChangeBlendMode(engine, block, it.blendMode) }
        it[BlockEvent.OnChangeOpacity::class] = { engine.block.setOpacity(block, it.opacity) }
        it[BlockEvent.OnChangeFillColor::class] = { onChangeFillColor(engine, block, it.color) }
        // and so on...
    }
}

private val blockEventHandler = BlockEventsHandler {
    it.engine = engine
    it.block = getBlockForEvents()
    it.fontFamilyMap = checkNotNull(assetsRepo.fontFamilies.value).getOrThrow()
}

open fun onEvent(event: Event) {
    // ...
    blockEventHandler.handleEvent(event)
}
```

### A Performance Boost

By harnessing the power of a `HashMap`, we've turbocharged our event handling. The time complexity for handling an event is now a lightning-fast (O(1)), a monumental improvement over the (O(n)) time complexity of the ponderous `when` statement. While our payload mechanism adds a dollop of syntactic sugar. It enables us to bundle all the necessary data into a single object, making our code more legible and maintainable.

? Note: Using a HashMap instead of a large when() statement provides a significant performance improvement. It can be up to 40 to 150 times faster. However, explaining the details would exceed the scope of this blog post. Therefore, I will cover it, along with other Kotlin performance puzzles, in a future blog post.

While the refactored code remains as simple as before:

```kotlin
onEvent(BlockEvent.OnChangeFillColor(Color.RED))
```

This still triggers the `handleEvent` method in `BlockEventsHandler`, which in turn performs the appropriate action based on the event type. The `BlockEvent` itself is a data object containing all event details, and it serves as the lambda parameter.

### A Note on Payloads

The payload creation is a dynamic lambda function that's executed each time an event is handled. This ensures that all variables not part of the event are consistently up-to-date. Given that we're dealing with a single thread per event handler, caching the payload is entirely secure.

## Step 3: Adding Syntactic Sweetness with Infix Functions

![sven2244_an_abstract_composition_resembling_a_candy_store_fille_894e8c68-0a37-4ad0-adc8-c313db1c29da.png](https://blog.img.ly/2023/09/4.png)

In our next act, we elevate our syntax to a new level of expressiveness and readability. We introduce an infix function called `to`, allowing us to map an event class to its corresponding action elegantly.

Witness the updated code:

```kotlin
abstract class EventsHandler<Payloads>(
    val fillPayload: (cache: Payloads) -> Unit
) {
    infix fun <Payloads, EventType : Event> KClass<out EventType>.to(lambda: Payloads.(event: EventType) -> Unit) {
        eventMap[event] = lambda as Payloads.(event: Event) -> Unit
    }
    // ... (rest of the code remains the same)
}

class BlockEventsHandler(
    manager: EventsManager,
    override val fillPayload: (cache: TextBlockEventsHandler) -> Unit
) : EventsHandler<TextBlockEventsHandler>(manager) {
    lateinit var engine: Engine
    lateinit var block: DesignBlock
    lateinit var fontFamilyMap: Map<String, FontFamilyData>

    init {
        BlockEvent.OnDelete::class to {
            engine.delete(block)
        }
        BlockEvent.OnBackward::class to {
            engine.sendBackward(block)
        }
        BlockEvent.OnDuplicate::class to {
            engine.duplicate(block)
        }
        BlockEvent.OnForward::class to {
            engine.bringForward(block)
        }
        BlockEvent.ToBack::class to {
            engine.sendToBack(block)
        }
        BlockEvent.ToFront::class to {
            engine.bringToFront(block)
        }
        BlockEvent.OnChangeFinish::class to {
            engine.editor.addUndoStep()
        }
        BlockEvent.OnChangeBlendMode::class to {
            onChangeBlendMode(engine, block, it.blendMode)
        }
        BlockEvent.OnChangeOpacity::class to {
            engine.block.setOpacity(block, it.opacity)
        }
        BlockEvent.OnChangeFillColor::class to {
            onChangeFillColor(engine, block, it.color)
        }
        // ...
    }
}
```

### Syntactic Sweetness and Performance

The introduction of the `to` infix function adds a sprinkle of syntactic sweetness that enhances code expressiveness and enables a more natural usage. This makes it crystal clear what each event is all about. And fear not, the performance remains at a blazing-fast (O(1)), thanks to our trusty HashMap.

### Flexibility in Syntax

While the `to` keyword is used here, feel free to substitute it with other terms like `handle`, `trigger`, or anything that best suits your context. Flexibility is the name of the game.

## Step 4: Embracing Inline Functions for Elegance

![sven2244_a_mesmerizing_image_of_a_kaleidoscope_with_vibrant_pat_b53a7e30-820a-4308-a7aa-11222be8a7a1.png](https://blog.img.ly/2023/09/5.png)

However, this is still not perfect because the `::class` breaks smooth reading.

So let's do it differently. Let us try to introduce a more elegant way to register an event. Let us eliminate the need to specify `::class` every time we register an event handler will make our code more concise and readable.

This is made possible by an inline function with a verified type parameter that maintains the class reference at runtime.

To do this, we extend the `EventsHandler` class with this new `register` function:

```kotlin
class EventsHandler(
    register: EventsHandler.() -> Unit,
) {
    inline fun <reified EventType : BaseEvent> register(noinline lambda: (event: EventType) -> Unit) : Any {
        this[EventType::class] = lambda
        return lambda
    }
   // ... (rest of the code remains the same)
}
```

### The New Syntax

This is what registering an event handler looks like with the new syntax:

```kotlin
register<BlockEvent.OnChangeLineWidth> {
    engine.block.setWidth(block, engine.block.getFrameWidth(block))
    engine.block.setHeight(block, it.width)
}
```

Much better, right? The new syntax is more concise, eliminates redundancy, and is type-safe because the reified type parameters ensure that the event type is known at compile-time and runtime, eliminating the need for unsafe casting.

## Step 5: Elevating `register` to an Extension Function for Highlighting

![highlighting.jpg](https://blog.img.ly/2023/09/6-1.jpg)

To improve code readability, we'll make a subtle but effective step by converting the `register` function from a `EventsHandler` class function, into an `EventsHandler` extension function.

Sounds stupid! So why?

This small change improves code readability by highlighting the `register` keyword through syntax highlighting from a Kotlin extension function. This will make it much more colorful, which improves readability.

### Updated `EventsHandler` Class

The `EventsHandler` class remains largely unchanged, but the `register` function is now outside the class and transformed into an extension function for the `EventsHandler` class:

```kotlin
class EventsHandler(
    register: EventsHandler.() -> Unit,
) {
    // ... (rest of the code remains the same)
}

inline fun <reified EventType : BaseEvent> EventsHandler.register(noinline lambda: (event: EventType) -> Unit) : Any {
    this[EventType::class] = lambda
    return lambda
}
```

By simply shifting `register` out of the class, the `EventsHandler` class definition now stands out with distinctive syntax highlighting. It's a clever trick that doesn't impact runtime or compile performance, since it's an inline operation anyway.

```kotlin
**register**<BlockEvent.OnChangeLineWidth> {
    engine.block.setWidth(block, engine.block.getFrameWidth(block))
    engine.block.setHeight(block, it.width)
}
```

## Step 6: Eliminating `lateinit` Variables with Delegated Properties

![sven2244_an_abstract_image_of_interconnected_gears_and_cogs_for_3b651ffc-9367-470d-8be3-82c3c19b9ffe.png](https://blog.img.ly/2023/09/7.png)

Now, it's time to address the enigmatic `lateinit` variables and the somewhat convoluted `fillPayload` mechanism. Let us introduce a cleaner approach, using delegated properties and lambda functions to inject dependencies.

Let's add an `Inject` class to wrap a normal lambda as delegable:

```kotlin
class Inject<Type>(private val inject: () -> Type) {
    operator fun getValue(thisRef: Any?, property: KProperty<*>): Type = inject()
}
```

With this newfound power, our event handler code becomes cleaner and more intuitive. It takes on the style of Jetpack Compose's declarative syntax:

```kotlin
fun EventsHandler.textBlockEvents(
    engine: () -> Engine,
    block: () -> DesignBlock,
    fontFamilyMap: () -> Map<String, FontFamilyData>,
) {
    // Inject the dependencies
    val engine by Inject(engine)
    val block by Inject(block)
    val fontFamilyMap by Inject(fontFamilyMap)

    // Event handling logic here
    // ...
}
```

Whenever one of the variables is accessed, the lambda is called, and you always get the current variable.

Also, the creation of the "payload" becomes more straightforward, clean, and type-safe. It kinda looks like passing a variable:

```kotlin
private val eventHandler = EventsHandler {
    textBlockEvents (
        engine = ::engine,
        block = ::getBlockForEvents,
        fontFamilyMap = { checkNotNull(assetsRepo.fontFamilies.value).getOrThrow() },
    )
}
```

Looks and feels like magic! Pretty cool, right?

## Step 7: Multiple Event Handlers for Single Responsibility Principle

![sven2244_code_as_a_magical_candy-filled_wonderland._Syntax_Suga_a50bcd92-bea2-4fea-aa92-e1cd557277b6.png](https://blog.img.ly/2023/09/8.png)

In our grand finale, we harness the newfound flexibility from our previous changes to register multiple event handler functions. Each event handler registration function now has a specific topic, aligning perfectly with the Single Responsibility Principle (SRP).

### Enhanced Event Handler Registration

We can now register multiple event handler functions within the same `EventsHandler` instance. Each function can specialize in handling a particular type of event, making the code more modular and manageable. Behold the grand design:

```kotlin
private val eventHandler = EventsHandler {
    cropEvents(
        engine = ::engine,
        block = ::getBlockForEvents,
    )
    blockEvents (
        engine = ::engine,
        block = ::getBlockForEvents,
    )
    textBlockEvents (
        engine = ::engine,
        block = ::getBlockForEvents,
        fontFamilyMap = { checkNotNull(assetsRepo.fontFamilies.value).getOrThrow() },
    )
    // ...
}

fun EventsHandler.blockEvents(
    engine: () -> Engine,
    block: () -> DesignBlock
) {
    val engine: Engine by Inject(engine)
    val block: DesignBlock by Inject(block)

    register<BlockEvent.OnDelete> { engine.delete(block) }

    register<BlockEvent.OnBackward> { engine.sendBackward(block) }

    register<BlockEvent.OnDuplicate> { engine.duplicate(block) }

    register<BlockEvent.OnForward> { engine.bringForward(block) }

    register<BlockEvent.ToBack> { engine.sendToBack(block) }

    register<BlockEvent.ToFront> { engine.bringToFront(block) }

    register<BlockEvent.OnChangeFinish> { engine.editor.addUndoStep() }

    register<BlockEvent.OnChangeBlendMode> {
        if (engine.block.getBlendMode(block) != it.blendMode) {
            engine.block.setBlendMode(block, it.blendMode)
            engine.editor.addUndoStep()
        }
    }

    register<BlockEvent.OnChangeOpacity> { engine.block.setOpacity(block, it.opacity) }
}

fun EventsHandler.cropEvents(
    engine: () -> Engine,
    block: () -> DesignBlock
) {
    val engine: Engine by Inject(engine)
    val block: DesignBlock by Inject(block)
    // ... (event handling logic for cropping events)
}

fun EventsHandler.textBlockEvents(
    engine: () -> Engine,
    block: () -> DesignBlock,
    fontFamilyMap: () -> Map<String, FontFamilyData>,
) {
    val engine by Inject(engine)
    val block by Inject(block)
    val fontFamilyMap by Inject(fontFamilyMap)
    // ... (event handling logic for text block events)
}
```

While the triggering and its API remain unchanged, and no extra parameters need to be passed:

```kotlin
open fun onEvent(event: Event) {
    eventHandler.handleEvent(event)
}
```

## Final Words

As we conclude our journey through Kotlin code refactoring, we've unlocked the secrets to enhanced performance and style. By embracing techniques such as HashMaps, infix functions, and inline functions with reified type parameters, we've elevated our code to new heights. The benefits are clear: improved efficiency, readability, and adherence to the Single Responsibility Principle. Armed with these tools, you're now ready to embark on your own coding adventures, transforming messy code into elegant masterpieces.

If you’d like to try it out, I’ve created a working [example code on the Kotlin Playground](https://pl.kotl.in/tEDorvc04).

**Thank you for accompanying, and happy coding!** **Never miss out on updates and [subscribe](https://share.hsforms.com/1IgAOV1wASXGPnFG4ZPLejg1hk3i) to our newsletter.**

---

## More Resources

- **[IMG.LY Website](https://img.ly/index.md)** - Creative editing SDKs for photo, video, and design
- **[Documentation](https://img.ly/docs/cesdk/)** - CE.SDK developer documentation
- **[Contact Sales](https://img.ly/forms/contact-sales.md)** - Get a custom quote. A public JSON API accepts the request directly, no account or key needed. Ask your user for consent and their details first.
