Friday, May 19, 2023

Strategy Design Pattern with Kotlin Comparative Analysis with Java

 Strategy Design Pattern

Getting Familiar with Behavioral Patterns we will discuss behavioral patterns with Kotlin. Behavioral patterns deal with how objects interact with one another.

 We'll see how an object can behave in a totally different manner based on the situation, how objects can communicate without knowledge of one another, and how we can iterate over complex structures easily. 

In this blog, we will cover the following topics: 

 Strategy 

 Iterator

 State 

 Command 

 Chain of responsibility 

 Interpreter 

 Mediator 

 Memento

 Visitor 

 Template method 

 Observer

 Strategy  

 Understanding Structural Patterns, while discussing the Facade design pattern?we discussed about a game logic and let say Abhinaw who acts as a game designer in our small indie game development company, came up with a great idea. What if we were to give our hero an arsenal of weapons to protect us from those horrible carnivorous snails? Weapons all shoot projectiles (you don't want to get close to those dangerous snails) in the direction our hero is facing: 

 enum class Direction {

    LEFT, RIGHT

All projectiles should have a pair of coordinates (our game is 2D, remember?) and a direction: 

abstract class Projectile(private val x: Int,

                          private val y: Int,

                          private val direction: Direction)


If we were to shoot only one type of projectile, that would be simple, since we already covered the Factory pattern earlier, Working with Creational Patterns: 

class OurHero {

    private var direction = Direction.LEFT

    private var x: Int = 42

    private var y: Int = 173

 fun shoot(): Projectile {

        return object : Projectile(x, y, direction) {

            // Draw and animate projectile here

        }

    }

But Abhinaw wants our hero to have at least three different weapons: 

Peashooter: Shoots small peas that fly straight. Our hero starts with it. 

Pomegranate: Explodes when hitting an enemy, much like a grenade. 

Banana: Returns like a boomerang when it reaches the end of the screen. Come on, Abhinaw, give us some slack! Can't you just stick with regular guns that all work the same?!

Fruit arsenal 

First, let's discuss how we could solve this in the Java way. In Java, we would have created an interface, that abstracts the changes. In our case, what changes is our hero's weapon: 

interface Weapon {

    fun shoot(x: Int,

              y: Int,

              direction: Direction): Projectile

Then all other weapons would implement this interface: 

class Peashooter : Weapon {

    override fun shoot(x: Int,

                       y: Int,

                       direction: Direction) = 

                        object : Projectile(x, y, direction) {

        // Fly straight

    }

}

class Pomegranate : Weapon {

    override fun shoot(x: Int,

                       y: Int, direction: Direction)  = 

                        object : Projectile(x, y, direction) {

        // Explode when you hit first enemy

    }

}

 class Banana : Weapon {

    override fun shoot(x: Int,

                       y: Int,

                       direction: Direction)  = 

                        object : Projectile(x, y, direction) {

        // Return when you hit screen border

    }

Then our hero would hold a reference to a weapon (Peashooter at the beginning): private var currentWeapon : Weapon = Peashooter() It would delegate the actual shooting process to it: 

fun shoot(): Projectile = currentWeapon.shoot(x, y, direction) 

What's left is the ability to equip another weapon: 

fun equip(weapon: Weapon) {

    currentWeapon = weapon

}      

And that's what the Strategy design pattern is all about. Now, our algorithms (Abhinaw's weapons, in that case) are interchangeable.

Citizen function 

With Kotlin, there's a more efficient way to implement the same functionality using fewer classes. That's thanks to the fact that functions in Kotlin are first-class citizens. What does that mean? For one, we can assign functions to the variables of our class, like any other normal value. It makes sense that you can assign a primitive value to your variable: val x = 7 You could either assign an object to it: 

var myPet = Canary("Michael") 

So, why should you be able to assign a function to your variable? As follows: 

val square = fun (x: Int): Long {

    return (x * x).toLong()

}

With Kotlin, this is totally valid.

Switching sides So, how do higher-order functions help us here? First, we'll define a namespace for all our weapons. This is not mandatory, but helps to keep everything in check:

object Weapons {

    // Functions we'll be there soon

Then, instead of classes, each of our weapons will become a function: 

val peashooter = fun(x: Int, y: Int, direction: Direction):Projectile {

        // Fly straight

}

 val banana = fun(x: Int, y: Int, direction: Direction): Projectile {

        // Return when you hit screen border

}

 val pomegranate = fun(x: Int, y: Int, direction: Direction):Projectile {

        // Explode when you hit first enemy

}

The most interesting part is our hero. It now holds two functions:

 class OurHero {

    // As before var currentWeapon = Weapons.peashooter

 val shoot = fun() {

        currentWeapon(x, y, direction)

    }

The interchangeable part is currentWeapon, while shoot is now an anonymous function that wraps it. To test that our idea works, we can shoot the default weapon once, then switch to Banana and shoot it again: 

val h = OurHero()

h.shoot()

h.currentWeapon = Weapons.banana

h.shoot() 

Notice that this dramatically reduces the number of classes we have to write, while keeping the functionality the same.

Code Examples :

1.In Kotlin :

fun main(args: Array<String>) {
val h = OurHero()
h.shoot()
h.currentWeapon = Weapons.banana
h.shoot()
}

object Weapons {
val peashooter = fun(x: Int, y: Int, direction: Direction) {
// Fly straight
}

val banana = fun(x: Int, y: Int, direction: Direction) {
// Return when you hit screen border
}

val pomegranate = fun(x: Int, y: Int, direction: Direction) {
// Explode when you hit first enemy
}
}

class OurHero {
private var direction = Direction.LEFT
private var x: Int = 42
private var y: Int = 173
var currentWeapon = Weapons.peashooter
val shoot = fun() {
currentWeapon(x, y, direction)
}
}

abstract class Projectile(
private val x: Int,
private val y: Int,
private val direction: Direction
) {
}

enum class Direction {
LEFT, RIGHT
}

2.In Java :

class OurHero {
private var direction = Direction.LEFT
private var x: Int = 42
private var y: Int = 173

private var currentWeapon : Weapon = Peashooter()

fun shoot(): Projectile = currentWeapon.shoot(x, y, direction)

fun equip(weapon: Weapon) {
currentWeapon = weapon
}
}

abstract class Projectile(private val x: Int,
private val y: Int,
private val direction: Direction) {

}

enum class Direction {
LEFT, RIGHT
}

interface Weapon {
fun shoot(x: Int,
y: Int,
direction: Direction): Projectile
}

class Peashooter : Weapon {
override fun shoot(x: Int,
y: Int,
direction: Direction) = object : Projectile(x, y, direction) {
// Fly straight
}
}

class Pomegranate : Weapon {
override fun shoot(x: Int,
y: Int,
direction: Direction) = object : Projectile(x, y, direction) {
// Explode when you hit first enemy
}
}

class Banana : Weapon {
override fun shoot(x: Int,
y: Int,
direction: Direction) = object : Projectile(x, y, direction) {
// Return when you hit screen border
}
}

This is the difference between both of them.

No comments: