idiomatic-kotlin-course/notebooks/Kotlin design patterns.ipynb
2024-11-12 09:20:43 +03:00

27 KiB

Sheet

Factories

In [ ]:
interface A{
    val body: String
}

class B(override val body: String): A{
    override fun toString() = "$body B"
}

class C(override val body: String): A{
    override fun toString() = "$body C"
}

fun print(obj: A){
    println(obj)
}
In [ ]:
print(C("My body"))
In [ ]:
interface AFactory<out T: A>{
    fun create(body: String): T
}

object BFactory: AFactory<B>{
    override fun create(body: String): B = B(body)
}

fun <T: A> createWithAWord(word: String, factory: AFactory<T>) = factory.create("Word: $word")
In [ ]:
createWithAWord("Hello", BFactory)
In [ ]:
fun <T: A> createWithAWord(word: String, factoryFunction: (String) -> T) = factoryFunction("Word: $word")
In [ ]:
createWithAWord("Hello", ::B)
In [ ]:
createWithAWord("Hello"){body -> B(">\t$body")}

Factory functions

In [ ]:
data class Client(val url: String)

interface Request{
    val client: Client
}

data class RequestImpl(override val client: Client): Request{
//    constructor(url: String): this(Client(url))
}
In [ ]:
fun Request(url: String): Request{
    return RequestImpl(Client(url))
}
In [ ]:
Request("DDD")
In [ ]:
listOf(1,2,3)

Lazy

In [ ]:
class WithLazy{
    val prop = 2
        get(){
            println("Hit $field")
            return field
        }

    val lazyProp by lazy{
        println("Hit lazy")
        2
    }
}
In [ ]:
val withLazy = WithLazy()
In [ ]:
withLazy.prop
withLazy.prop
withLazy.prop
In [ ]:
withLazy.lazyProp
withLazy.lazyProp
withLazy.lazyProp

Builder

In [ ]:
data class Buildable(
    var a: Int = 1,
    var b: Int = 2,
    var c: Int = 3
){
    fun setABC(value: Int){
        a = value
        b = value
        c = value
    }
}

// Buildable.builder()
//     .setA(1)
//     .setB(2)
//     .setC(3)
//     .setABC(1)
//     .build()
In [ ]:
Buildable(b = 5)
In [ ]:
Buildable().apply { 
    a = 1
    setABC(a + b + c)
}
In [ ]:
fun Buildable(block: Buildable.()-> Unit): Buildable = Buildable().apply(block)

Buildable{ 
    a = 1
    setABC(a + b + c)
}

Multiton

In [ ]:
class Multiton private constructor(val name: String){
    init{
        println("Multiton $name created")
    }
    
    companion object {
        private val cache = HashMap<String, Multiton>()

        fun resolve(name: String) = cache.getOrPut(name){Multiton(name)}
    }
}
In [ ]:
Multiton.resolve("a")
Multiton.resolve("a")
Multiton.resolve("b")
Multiton.resolve("a")
In [ ]:
Multiton("v")
In [ ]:
sealed interface SealedMultiton{
    object A: SealedMultiton
    object B: SealedMultiton
    class Custom(val body: String): SealedMultiton
}

fun evaluateSealedMultiton(sealed: SealedMultiton) = when(sealed){
    is SealedMultiton.A-> println("A")
    is SealedMultiton.B-> println("B")
    is SealedMultiton.Custom -> println(sealed.body)
}

Adapter

In [ ]:
val array = doubleArrayOf(1.0,2.0)
val list = listOf(1.0,2.0)


fun consumeList(l: List<Double>){
    println(l)
}

fun convertArrayToList(array: DoubleArray): List<Double> = array.asList()//List(array.size){i -> array[i]}

consumeList(convertArrayToList(array))

Decorator?

In [ ]:
@JvmInline
value class EMail(val address: String)

fun EMail.validate(): Boolean = TODO()

val EMail.server get() = address.substringAfter("@")

Delegate

In [ ]:
class ValueHolder{
    var value = 2

    fun printValue(){
        println(value)
    }
}

ValueHolder().apply { value = 6 }.printValue()

Bad example

In [ ]:
class ModifiedValueHolder: ValueHolder(){
    override fun printValue(){
        print("value = ")
        super.printValue()
    }
}

ModifiedValueHolder().apply { value = 6 }.printValue()
In [ ]:
class GoodValueHolder(val valueHolder: ValueHolder = ValueHolder()){

    var value by valueHolder::value

    fun printValue(){
        print("value = ")
        valueHolder.printValue()
    }
}
GoodValueHolder().apply { value = 6 }.printValue()
In [ ]:
class CompositeValueHolder(
    val a: ValueHolder,
    val b: ValueHolder
){
    fun printValue(){
        print("a = ")
        a.printValue()
        println()
        print("b = ")
        a.printValue()

    }
}
In [ ]:
class Id(val value: String)
In [ ]:
Id("d3d").value
In [ ]:
Id("fff").length

Proxy

In [ ]:
class StringProxy(val string: String): CharSequence by string{
    override fun get(index: Int): Char{
        return if(index < 0)  '!' else string[index]
    }
}
In [ ]:
val proxy = StringProxy("dddd")

println(proxy[0])
println(proxy[-1])

Observer

In [ ]:
class ObservableValueHolder(defaultValue: Int, val callback: (Int) -> Unit){
    var value: Int = defaultValue
        set(value){
            field = value
            callback(value)
        }
}
In [ ]:
val holder = ObservableValueHolder(1){
    println("Changed value = $it")
}
holder.value = 6
holder.value = 7

Visitor

Classic visitor

In [ ]:
interface Structure

class StringStructure(val string: String): Structure

class IntStructure(val int: Int): Structure

class StructureList(val content: List<Structure>): Structure
In [ ]:
fun interface StructureVisitor{
    fun visit(structure: Structure): Unit
}

val myStructureVisitor = object: StructureVisitor{
    override fun visit(structure: Structure){
        when(structure){
            is StringStructure -> println("It is a StringStructure with value ${structure.string}")
            is IntStructure -> println("It is a IntStructure with value ${structure.int}")
            is StructureList -> structure.content.forEach{ visit(it) }
            else -> println("I don't know what it is")
        }
    }
}

fun StructureList.visit(visitor: StructureVisitor){
    visitor.visit(this)
}
In [ ]:
fun List<Structure>.asStructure() = StructureList(this)

@JvmName("stringsAsStruct")
fun List<String>.asStructure() = StructureList(map{StringStructure(it)})

val structureList = StructureList(
    listOf(
        StringStructure("ddd"),
        IntStructure(2),    
        IntStructure(7),
        listOf("aaa", "bbb").asStructure()
    )
)

structureList.visit(myStructureVisitor)

Kotlin idiomatic visiting

In [ ]:
sealed interface SealedStructure

class StringSealedStructure(val string: String): SealedStructure

class IntSealedStructure(val int: Int): SealedStructure
In [ ]:
fun List<SealedStructure>.visit(visitor: (SealedStructure) -> Unit){
    forEach(visitor)
}
In [ ]:
val sealedStructureList = listOf<SealedStructure>(
    StringSealedStructure("ddd"),
    IntSealedStructure(2),    
    IntSealedStructure(7),
)

sealedStructureList.visit{ structure ->
    when(structure){
        is StringSealedStructure -> println("It is a StringSealedStructure with value ${structure.string}")
        is IntSealedStructure -> println("It is a IntSealedStructure with value ${structure.int}")
        else -> {} // why is that?
    }
}
In [ ]:

Observer

In [ ]:
import kotlin.properties.Delegates

class ObservableHolder{
    var observable: Int by Delegates.observable<Int>(1){ property, oldValue, newValue ->
        println("${property.name} $newValue")
    }
}
In [ ]:
val holder = ObservableHolder()

holder.observable = 5
In [ ]:
class Response(val header: String, val body: () -> String ){
    fun onRead(callback: (String) -> Unit){
        callback(body())
    }
}

class Call(val request: String, val onResponse: (Response) -> Unit){
    fun start(){
        onResponse(Response("header"){"body"})
    }
}

Call("spc"){ response->
    println(response.header)
    response.onRead{ body->
        println(body)
    }
}.start()
In [ ]: