Kotlin Design Patterns: Bridge Explained

Michal Ankiersztajn on 2024-03-11

Structural Design Patterns

Kotlin Design Patterns: Bridge Explained

Why should you use Bridge?

The primary purpose of this pattern is to split a class into two abstract hierarchies of classes so that they can grow independently. It separates the responsibilities of a single class into multiple classes with a single responsibility.

Consider using this pattern if your class is becoming too big or too complex.

Advantages

You’re working on a project, and your task is to implement a Table and Chair production feature. Both of them can be created from Wood and Metal. You’ve also heard that they’ll likely add more types of Furniture and Materials.

That means adding new Material shouldn’t interfere with Furniture and the other way around.

Here’s a class diagram of how we’ll structure our code:

Bridge diagram

We could also keep adding WoodenChair , MetalChair etc., but then we’ll need Materials * Furniture number of classes instead of Materials + Furniture number of classes. Our code will grow linearly instead of exponentially.

You could also make the Furniture have a Material instead of making the concrete classes do that. Then the diagram would look like this:

Bridge with interface aggregation

I prefer the first one because it’s better when concrete classes depend on others, not abstract ones. Let’s start by coding Material:

interface Material {
    fun collect(): String
}

class Wood : Material {
    override fun collect() = "wood"
}

class Metal : Material {
    override fun collect() = "metal"
}

Now that we have all the required Material classes, let’s create Furniture :

interface Furniture {
    fun build()
}

class Chair(
    private val material: Material,
) : Furniture {
    override fun build() {
        println("Building Chair from " + material.collect())
    }
}

class Table(
    private val material: Material,
) : Furniture {
    override fun build() {
        println("Building Table from " + material.collect())
    }
}

// Usage
fun main() {
    val woodenTable: Furniture = Table(material = Wood())
    val metalTable: Furniture = Table(material = Metal())
    woodenTable.build() // Building Table from wood
    metalTable.build() // Building Table from metal

    val woodenChair: Furniture = Chair(material = Wood())
    val metalChair: Furniture = Chair(material = Metal())
    woodenChair.build() // Building Chair from wood
    metalChair.build() // Building Chair from metal
}

Now, it’s just an example, so I’m keeping things simple, but Table , Chair might have more details same goes for Metal and Wood . In that case, it’ll also help separate some logic.

Thanks for reading! I hope you’ve learned something new. Please clap and follow me for more!

More design patterns

Design Patterns In Kotlin List of creational, structural and behavioral design patterns implemented in Kotlinmedium.com

Based on the book:

“Wzorce projektowe : elementy oprogramowania obiektowego wielokrotnego użytku” — Erich Gamma Autor; Janusz Jabłonowski (Translator); Grady Booch (Introduction author); Richard Helm (Author); Ralph Johnson (Author); John M Vlissides (Author)