Europe: +41 78 715 83 09 - Asia: +84 ‎975 112 112
contact@finix.asia

Blog

5 Jan 2018

SOLID: Dependency Inversion Principle

//
Comments0

Technologies used:   Kotlin 1.2.10 | Maven 3.3.9 | Spek 1.1.5

Dependency Inversion Principle

As a Java programmer, you’ve likely heard about code coupling and have been told to avoid tightly coupled code. Ignorance of writing “good code” is the main reason of tightly coupled code existing in applications. As an example, creating an object of a class using the new operator results in a class being tightly coupled to another class. Such coupling appears harmless and does not disrupt small programs. But, as you move into enterprise application development, a tightly coupled code can lead to serious adverse consequences.

When one class knows explicitly about the design and implementation of another class, changes to one class raise the risk of breaking the other class. Such changes can have rippling effects across the application making the application fragile. To avoid such problems, you should write “good code” that is loosely coupled, and to support this you can turn to the Dependency Inversion Principle.

The principle states:

“A. High-level modules should not depend on low-level modules. Both should depend on abstractions.
B. Abstractions should not depend on details. Details should depend on abstractions.”

Bad Example


package vn.finixasia.didemo.bean.light

class LightBulb {
fun turnOn() {
println("LightBulb: Bulb turned on...")
}

fun turnOff() {
println("LightBulb: Bulb turned off...")
}
}

And we write an ElectricPowerSwitch as follow

package vn.finixasia.didemo.bean.light

class ElectricPowerSwitch(var lightBulb: LightBulb) {
    var isOn: Boolean = false

    init {
        this.isOn = false
    }

    fun press() {
        val checkOn = isOn
        if (checkOn) {
            lightBulb.turnOff()
            this.isOn = false
        } else {
            lightBulb.turnOn()
            this.isOn = true
        }
    }
}

As you notice, the ElectricPowerSwitch is tidy coupled to the light bulb, if we wish to add a new device, then we will need to change again and again for each device the ElectricPowerSwitch.

Better practices

To improve our above example we will create two interfaces as follow:

package vn.finixasia.didemo.bean.light

interface Switch {
    val isOn: Boolean
    fun press()
}
package vn.finixasia.didemo.bean.light

interface Switchable {
    fun turnOn()
    fun turnOff()
}

Now our ElectricPowerSwitch or any other Switch will implement them as follow

package vn.finixasia.didemo.bean.light

class ElectricPowerSwitch(var client: Switchable) : Switch {
    override var isOn: Boolean = false
    init {
        this.isOn = false
    }

    override fun press() {
        val checkOn = isOn
        if (checkOn) {
            client.turnOff()
            this.isOn = false
        } else {
            client.turnOn()
            this.isOn = true
        }
    }
}

Now we are not anymore worry to modify the Switch on each new device

For our example, we implement two devices and then test them

package vn.finixasia.didemo.bean.light

class LightBulb: Switchable {
    override fun turnOn() {
        println("LightBulb: Bulb turned on...")
    }

    override fun turnOff() {
        println("LightBulb: Bulb turned off...")
    }
}

And

package vn.finixasia.didemo.bean.light

class FanSwitch: Switchable {
    override fun turnOn() {
        println("FanSwitch: Fan turned on...")
    }

    override fun turnOff() {
        println("FanSwitch: Fan turned off...")
    }
}

The test

package vn.finixasia.didemo.bean.light

import org.assertj.core.api.Assertions.*
import org.jetbrains.spek.api.Spek
import org.jetbrains.spek.api.dsl.describe
import org.jetbrains.spek.api.dsl.it
import org.jetbrains.spek.api.dsl.on

object ElectricPowerSwitchTest : Spek({
    describe("An ElectricPowerSwitch"){

        on("a LightBulb") {
            var switch = ElectricPowerSwitch(LightBulb())
            it("switch on/off"){
                assertThat(switch.isOn).isFalse()
                switch.press()
                assertThat(switch.isOn).isTrue()
                switch.press()
                assertThat(switch.isOn).isFalse()
            }
        }
        on("a Fan") {
            var switch = ElectricPowerSwitch(FanSwitch())
            it("switch on/off"){
                assertThat(switch.isOn).isFalse()
                switch.press()
                assertThat(switch.isOn).isTrue()
                switch.press()
                assertThat(switch.isOn).isFalse()
            }
        }

    }
})

All is green

Leave a Reply

Translate »