Friday, May 12, 2023

Composite Design Pattern in Kotlin comparative analysis with Java

 Composite

You may finish this section with a lingering feeling that this pattern is a bit awkward. That's because it has a soul mate, it's accompanying pattern, Iterator, which we'll discuss in next post. When both are combined, that's where they really shine. So, if you're feeling confused, come back to this pattern after you have got acquainted with Iterator too. Having said that, we can start analyzing this pattern. It may look a bit strange to have a Composite design pattern. After all, aren't all Structural Patterns about composing objects? Much like in the case of the Bridge design pattern, the name may not reflect its true benefits.

Going back to our strategy game, we have a new concept: a squad. 

A squad consists of zero or more infantry units. This would be a good example of a somewhat complex data structure. 

Here are the interfaces and classes we have: 

interface InfantryUnit

 class Rifleman : InfantryUnit

 class Sniper : InfantryUnit 

How would you implement that? 

The Squad Squad, 

clearly, must be a collection of infantry units. So, it should be easy: 

class Squad(val infantryUnits: MutableList<InfantryUnit> = mutableListOf()) 

We even set up a default parameter value, so the other programmer won't need to pass his own list of soldiers unless he really needs too. MutableList suits us well here, since we may add units later. To make sure it works, we'll create three soldiers and put them inside: 

val miller = Rifleman()

val caparzo = Rifleman()

val jackson = Sniper()

val squad = Squad()

squad.infantryUnits.add(miller)

squad.infantryUnits.add(caparzo)

squad.infantryUnits.add(jackson)

println(squad.infantryUnits.size) 

// Prints 3 

But the next day, Someone , that's the other programmer, comes to us with a new requirement. He thinks it consists of too many lines of code to add soldiers one by one, or even to use mutableListOf() to pass them. He would like to initialize squads like this: val squad = Squad(miller, caparzo, jackson)

That looks nice, but how in the name of all the squads are we going to do that?

Varargs and secondary constructors 

Up until now, we were always using the primary constructor of the class. That's the one declared after the class name. But in Java, we can define more than one constructor for a class. Why does Kotlin limit us to only one? Actually, it doesn't. We can define secondary constructors for a class using the constructor keyword: 

class Squad(val infantryUnits: MutableList<InfantryUnit> = mutableListOf()) {

    constructor(first: InfantryUnit) : this(mutableListOf()) {

        this.infantryUnits.add(first)

    }

 constructor(first: InfantryUnit, 

                second: InfantryUnit) : this(first) {

        this.infantryUnits.add(second)

    }

 constructor(first: InfantryUnit, 

                second: InfantryUnit, 

                third: InfantryUnit) : 

        this(first, second) {

        this.infantryUnits.add(third)

    }

}

 Note how we delegate one constructor to another:  

 constructor(first: InfantryUnit) : this(mutableListOf()) {

    }                                     ⇑

                                          ⇑

    constructor(first: InfantryUnit,      ⇑ // Delegating

                second: InfantryUnit) : this(first) {    

    } 

But this is clearly not the way to go, since we cannot predict how many more elements Dave may pass us. If you come from Java, you have probably thought about variadic functions already, which can take an arbitrary number of arguments of the same type. In Java, you would declare the parameter as InfantryUnit... units. Kotlin provides us with the vararg keyword for the same purposes. Combining those two approaches, we get the following nice piece of code: 

class Squad(val infantryUnits: MutableList<InfantryUnit> = mutableListOf()) {

 constructor(vararg units: InfantryUnit) : this(mutableListOf()) {

        for (u in units) {

            this.infantryUnits.add(u)

        }

    }

}

Counting bullets 

The game designer catches you in the evening, when you are about to go home of course. He wants to add the ammo count for the entire squad so each squad will be able to report how much ammo it has left:

 fun bulletsLeft(): Long {

    // Do your job

}

Where's the catch? Well, you see, snipers have ammo as separate bullets: class Bullet Riflemen hold their bullets in magazines: 

class Magazine(capacity: Int) {

 private val bullets = List(capacity) { Bullet() } 

Luckily, you don't have machine gunners on your squad yet, because they carry their ammo in belts... So, you have a complex structure, which may or may not be nested. And you need to perform a certain operation on this structure as a whole. Here's where the Composite design pattern truly comes into play. You see, the name is a bit confusing. Composite is not so much about composing objects, but more about treating different type objects as nodes of the same tree. For that, they should all implement the same interface. It may not be that obvious at first. After all, a rifleman is clearly not a squad. But instead of looking at an interface as an is-a relationship, we should look at it as an ability enabler. Android, for example, adopts this pattern often. Our ability is to count bullets:

interface CanCountBullets {

    fun bulletsLeft(): Int

Both Squad and InfantryUnit should implement this interface: 

interface InfantryUnit : CanCountBullets

 class Squad(...) : CanCountBullets {

    ...

}

class Magazine(...): CanCountBullets { ... } 

And now, since everybody has the same ability, no matter how deep the nesting, we can ask the top-level object to query everything beneath it. Magazine and Sniper simply count the bullets they contain. The following example shows how we can keep a track of the number of bullets in Magazines: 

class Magazine(...): CanCountBullets {

    ... override fun bulletsLeft() = bullets.size

The following example shows how we can keep a track of the number of bullets Sniper has: 

class Sniper(initalBullets: Int = 50) : InfantryUnit {

    private val bullets = List(initalBullets) { Bullet () } override fun bulletsLeft() = bullets.size

For Rifleman, we can go over their Magazines and check how many bullets they contain: class Rifleman(initialMagazines: Int = 3) : InfantryUnit {

    private val magazines = List<Magazine>(initialMagazines) {

        Magazine(5)

    }

 override fun bulletsLeft(): Int {

        return magazines.sumBy { it.bulletsLeft() }

    }

Finally, for the squad, we count the sum of counts of all the units squad contains: 

override fun bulletsLeft(): Int {

    return infantryUnits.sumBy { it.bulletsLeft() }

Tomorrow, when your product manager discovers suddenly that he needs to implement a platoon (that's a collection of squads), you'll be armed and ready. 

Code Example :

fun main(args: Array<String>) {

val miller = Rifleman()
val caparzo = Rifleman()
val jackson = Sniper()

val squad = Squad(miller, caparzo, jackson)

println(squad.bulletsLeft())
}


class Squad(private val infantryUnits: MutableList<InfantryUnit> =
 mutableListOf()) : CanCountBullets {
    override fun bulletsLeft(): Int {
return infantryUnits.sumBy { it.bulletsLeft() }
}

constructor(vararg units: InfantryUnit) : this(mutableListOf()) {
for (u in units) {
this.infantryUnits.add(u)
}
}
}

class Bullet

class Magazine(capacity: Int) : CanCountBullets {
private val bullets = List(capacity) { Bullet() }

override fun bulletsLeft() = bullets.size
}

interface CanCountBullets {
fun bulletsLeft(): Int
}

interface InfantryUnit : CanCountBullets

class Rifleman(initialMagazines: Int = 3) : InfantryUnit {
private val magazines = List(initialMagazines) {
Magazine(5)
}

override fun bulletsLeft(): Int {
return magazines.sumBy { it.bulletsLeft() }
}
}

class Sniper(initalBullets: Int = 50) : InfantryUnit {

private val bullets = List(initalBullets) { Bullet() }

override fun bulletsLeft() = bullets.size
}

Output : 80


No comments: