Skip to content

Instantly share code, notes, and snippets.

@akueisara
Last active November 4, 2023 12:14
Show Gist options
  • Save akueisara/1832b1d5cc4f1da60eeb7917925ec3dc to your computer and use it in GitHub Desktop.
Save akueisara/1832b1d5cc4f1da60eeb7917925ec3dc to your computer and use it in GitHub Desktop.

Singleton

object MySingelton

Factory

interface Animal {
   val id : Int
   val name : String
}

class Cat(override val id: Int) : Animal { 
    // Same as before
}

class Dog(override val id: Int) : Animal {
    // Same as before
}

class DogFactory {
    fun createDog(breed: String, id: Int) = when(breed.trim().toLowerCase()) {
        "beagle" -> Beagle(id)
        "bulldog" -> Bulldog(id)
        else -> throw RuntimeException("Unknown dog breed $breed")
    }
}

class AnimalFactory {
    var counter = 0
    private val dogFactory = DogFactory()
    private val catFactory = CatFactory()

    fun createAnimal(animalType: String, animalBreed: String) : Animal {
        return when(animalType.trim().toLowerCase()) {
            "cat" -> catFactory.createDog(animalBreed, ++counter)
            "dog" -> dogFactory.createDog(animalBreed, ++counter)
            else -> throw RuntimeException("Unknown animal $animalType")
        }
    }
}

Static Factory

class NumberMaster {
    companion object {
        fun valueOf(hopefullyNumber: String) : Long {
            return hopefullyNumber.toLong()
        }
    }
}

println(NumberMaster.valueOf("123")) // Prints 123

Abstract Factory

Example 1

Assume we have a configuration for our server written in a YAML file:

server:
    port: 8080
environment: production

Our task is to construct objects from this configuration.

interface Property {
    val name: String
    val value: Any
}

interface ServerConfiguration {
    val properties: List<Property>
}

data class PropertyImpl(
    override val name: String,
    override val value: Any
) : Property

data class IntProperty(
    override val name: String,
    override val value: Int
) : Property

data class StringProperty(
    override val name: String,
    override val value: String
) : Property

data class ServerConfigurationImpl(
    override val properties: List<Property>
) : ServerConfiguration

class Parser {

    companion object {

        fun property(prop: String): Property {
            val (name, value) = prop.split(":")
            return when (name) {
                "port" -> IntProperty(name, value.trim().toInt())
                "environment" -> StringProperty(name, value.trim())
                else -> throw RuntimeException("Unknown property: $name")
            }
        }

        fun server(propertyStrings: List<String>):
            ServerConfiguration {
               val parsedProperties = mutableListOf<Property>()
               for (p in propertyStrings) {
                   parsedProperties += property(p)
               }
               return ServerConfigurationImpl(parsedProperties)
            }

    }

}

val portProperty = Parser.property("port: 8080")
val portOrNull: Int? = portProperty.value as? Int
if (portOrNull != null) {
    val port: Int = portOrNull // works
    println(port)
}

val environment = Parser.property("environment: production")
println(environment)

val server = Parser.server(listOf("port: 8080", "environment: production"))
println(server)

interface Parser {
    fun property(prop: String): Property
    fun server(propertyStrings: List<String>): ServerConfiguration
}

class YamlParser : Parser {
    // Implementation specific to YAML files
}

class JsonParser : Parser {
    // Implementation specific to JSON files
}

/*
interface Parser {
    fun property(prop: String): Property
    fun server(propertyStrings: List<String>): ServerConfiguration
}

class YamlParser : Parser {
    // Implementation specific to YAML files
}

class JsonParser : Parser {
    // Implementation specific to JSON files
}
*/

Example 2

interface HQ {
    fun buildBarracks(): Building<InfantryUnits, Infantry>
    fun buildVehicleFactory(): Building<VehicleUnits, Vehicle>
}

class HQ {
    val buildings = mutableListOf<Building<*, Unit>>()

    // Original
    fun buildBarracks(): Barracks {
        val b = Barracks()
        buildings.add(b)
        return b
    }
    
    // Improved
    fun buildBarracks(): Building<InfantryUnits, Infantry> {
      val b = object : Building<InfantryUnits, Infantry> {
        override fun build(type: InfantryUnits): Infantry {
            return when (type) {
                InfantryUnits.RIFLEMEN -> Rifleman()
                InfantryUnits.ROCKET_SOLDIER -> RocketSoldier()
            }
         }
      }
      buildings.add(b)
      return b
   }

    fun buildVehicleFactory(): VehicleFactory {
        val vf = VehicleFactory()
        buildings.add(vf)
        return vf
    }
    
    fun buildVehicleFactory(): Building<VehicleUnits, Vehicle> {
        val vf = object : Building<VehicleUnits, Vehicle> {
            override fun build(type: VehicleUnits) = when (type) {
                VehicleUnits.APC -> APC()
                VehicleUnits.TANK -> Tank()
            }
        }
        buildings.add(vf)

        return vf
     }
}

interface Unit 

interface Vehicle : Unit

interface Infantry : Unit

class APC : Vehicle

class Tank : Vehicle

enum class VehicleUnits {
    APC,
    TANK
}

class Rifleman : Infantry

class RocketSoldier : Infantry

enum class InfantryUnits {
    RIFLEMEN,
    ROCKET_SOLDIER
}

class Barracks : Building<InfantryUnits, Infantry> {
    override fun build(type: InfantryUnits): Infantry {
        return when (type) {
            RIFLEMEN -> Rifleman()
            ROCKET_SOLDIER -> RocketSoldier()
        }
    }
}

val hq = HQ()
val barracks1 = hq.buildBarracks()
val barracks2 = hq.buildBarracks()
val vehicleFactory1 = hq.buildVehicleFactory()

interface Building<in UnitType, out ProducedUnit> 
        where UnitType : Enum<*>, ProducedUnit : Unit {
    fun build(type: UnitType) : ProducedUnit
}

Generics

class Box<T> { 
    private var inside: T? = null 
 
    fun put(t: T) { 
        inside = t 
    }    
    fun get(): T? = inside 
} 

val box = Box<Cat>() 
box.put(Cat())
val cat = box.get()

State

State is a behavioral design pattern that lets an object alter its behavior when its internal state changes. It appears as if the object changed its class.

interface WhatCanHappen 
    fun seeHero()
    fun getHit(pointsOfDamage: Int)
    fun calmAgain()
}

class Snail : WhatCanHappen {
    private var mood: Mood = Still
    private var healthPoints = 10

    override fun seeHero() {
        mood = when(mood) {
            is Still -> Aggressive
            else -> mood
        }
    }

    override fun getHit(pointsOfDamage: Int) {
        healthPoints -= pointsOfDamage
        mood = when {
            (healthPoints <= 0) -> Dead
            mood is Aggressive -> Retreating
            else -> mood
        }
    }

    override fun calmAgain() {
    }
}

sealed class Mood {
   // Some abstract methods here, like draw(), for example
}

object Still : Mood()
object Aggressive : Mood()
object Retreating : Mood()
object Dead : Mood()

/*
// Another approach
class Snail {
    internal var mood: Mood = Still(this)
    private var healthPoints = 10
    // That's all!
}

sealed class Mood : WhatCanHappen

class Still(private val snail: Snail) : Mood() {

    override fun seeHero() {
        snail.mood = Aggressive
    }

    override fun getHit(pointsOfDamage: Int) {
        // Same logic from before
    }

    override fun calmAgain() {  
       // Return to Still state
    }
}
*/

Strategy

Allow an object to alter its behavior at runtime.

enum class Direction {
    LEFT, RIGHT
}

data class Projectile(
    private var x: Int,
    private var y: Int,
    private var direction: Direction
)

object Weapons {

    // Flies straight
    fun peashooter(x: Int, y: Int, direction: Direction):
        Projectile {
        return Projectile(x, y, direction)
    }

    // Returns back after reaching end of the screen
    fun banana(x: Int, y: Int, direction: Direction):
        Projectile {
        return Projectile(x, y, direction)
    }

    // Other similar implementations here  
}

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)
    }
}

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

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

// Returns back after reaching end of the screen
class Banana : Weapon {
    override fun shoot(
        x: Int,
        y: Int,
        direction: Direction
    ) = Projectile(x, y, direction)
}

val hero = OurHero()
hero.shoot()
hero.currentWeapon = Weapons::banana
hero.shoot()

Delegation

A class Derived can implement an interface Base by delegating all of its public members to a specified object:

interface Base {
    fun print()
}

class BaseImpl(val x: Int) : Base {
    override fun print() { print(x) }
}

class Derived(b: Base) : Base by b

fun main() {
    val b = BaseImpl(10)
    Derived(b).print()
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment