Blog Infos
Author
Published
Topics
Published
Topics

Not Demeter but I liked the pic! Photo by Nils on Unsplash

 

First of all, what is Law of Demeter?

It’s also called Demeter’s law or LoD or Principle of Least Knowledge or “don’t talk to strangers”.

It was discovered in 1987 in Northeastern University, Boston, by Ian Holland

Here is the general formulation of the law:

  • Only talk to your immediate friends.
  • Each unit should have only limited knowledge about other units: only units “closely” related to the current unit.
  • Or: Each unit should only talk to its friends; Don’t talk to strangers.
Violating the law, example in Kotlin

Law of Demeter

 

So, we’re easily violating this law and we don’t even notice most of the times.

Here is an example:

data class Car(val name: String, val engine: Engine)

data class Engine(val cc: CylinderCapacity, val power: Power)

data class CylinderCapacity(val value: Int)

data class Power(val value: Int)

fun main() {
    val car = Car(name = "Polo", engine = Engine(cc = CylinderCapacity(value = 2000), power = Power(200)))

    println(car.engine.power.value)
}

The fact that Car knows about Power and we can access the value in it directly through Car, is a violation of Law of Demeter. Why?
Let’s review the law:

Only talk to your immediate friends. Car’s immediate friends is Engine, not what’s contained in Engine (CylinderCapacity and Power)

We’re calling Power through Car, from the main function

OUR VIDEO RECOMMENDATION

Jobs

No results found.

How to fix it

Here is a possible way to fix it:

data class Car(private val name: String, private val engine: Engine) {
    fun power(): String {
        return "Car $name has ${engine.power()}"
    }
}

data class Engine(private val cc: CylinderCapacity, private val power: Power) {
    fun power(): String {
        return "${power.value}hp"
    }
}

data class CylinderCapacity(val value: Int)

data class Power(val value: Int)

fun main() {
    val car = Car(name = "Polo", engine = Engine(cc = CylinderCapacity(value = 2000), power = Power(200)))

    println(car.power())
}

So first of all let’s make some property private, it will help to avoid making them accessible from “strangers”:

data class Car(private val name: String, private val engine: Engine) {

data class Engine(private val cc: CylinderCapacity, private val power: Power) {

and expose them this way:

data class Car(private val name: String, private val engine: Engine) {
    fun power(): String {
        return "Car $name has ${engine.power()}"
    }
}

data class Engine(private val cc: CylinderCapacity, private val power: Power) {
    fun power(): String {
        return "${power.value}hp"
    }
}

fun main() {
    val car = Car(name = "Polo", engine = Engine(cc = CylinderCapacity(value = 2000), power = Power(200)))

    println(car.power())
}

Car calls Engine to know about Power, not Power directly.

 

Benefits of Law of Demeter

There are a lot of benefits but I’d like to highlight couple of them here.

Loose Coupling

Car is not directly coupled with Power. It’s true that Car knows about the concept of power, but it’s semantically correct since any Car has “power”, but the definition, rules, etc. are within the Engine.

For instance in the example the power is expressed in hp (Horse Power), so if we want to change that we just need to change Engine (maybe is better to move it to Power class? Maybe.), not Car.

data class Engine(private val cc: CylinderCapacity, private val power: Power) {
    fun power(): String {
        return "${power.value}hp"
    }
}

// From hp to kW.
data class Engine(private val cc: CylinderCapacity, private val power: Power) {
    fun power(): String {
        return "${power.value}kW"
    }
}

Here we’ve changed the power from hp to kW.

Encapsulation

Very similar to the previous point, if you change Power, should not affect Car. In this case we want to change Engine to take into account different types of powers (fuel vs hybrid for instance).

data class Car(private val name: String, private val engine: Engine) {
    fun power(): String = "Car $name has ${engine.power()}"
}

data class Engine(private val cc: CylinderCapacity, private val powerSources: List<PowerSource>) {
    fun power(): String = "${powerSources.sumOf { it.power() }}hp"
}

data class CylinderCapacity(private val value: Int)

data class ElectricPowerSource(private val value: Int) : PowerSource {
    override fun power() = value
}

data class FuelPowerSource(private val value: Int) : PowerSource {
    override fun power() = value
}

interface PowerSource {
    fun power(): Int
}

fun main() {
    val powerSources = listOf(
        ElectricPowerSource(300),
        FuelPowerSource(200),
    )
    val car = Car(name = "Polo", engine = Engine(cc = CylinderCapacity(value = 2000), powerSources = powerSources))

    println(car.power())
}

Even if in this case the logic is really dirty, no change is required in Car, since it’s just calling the .power() method that is calling the engine’s one that is encapsulating the complexity of calculating the power of the engine, that could come from multiple power sources.

println(car.power())
Did you like this article?

Photo by Nils on Unsplash

This article was previously published on proandroiddev.com

YOU MAY BE INTERESTED IN

YOU MAY BE INTERESTED IN

blog
It’s one of the common UX across apps to provide swipe to dismiss so…
READ MORE
blog
Hi, today I come to you with a quick tip on how to update…
READ MORE
blog
Automation is a key point of Software Testing once it make possible to reproduce…
READ MORE
blog
Drag and Drop reordering in Recyclerview can be achieved with ItemTouchHelper (checkout implementation reference).…
READ MORE

Leave a Reply

Your email address will not be published. Required fields are marked *

Fill out this field
Fill out this field
Please enter a valid email address.

Menu