Implementing observable คุณสมบัตินั่นได้ยัง serialize ใน Kotlin

0

คำถาม

ฉันพยายามจะสร้างชั้นเรียนอยู่ไหนแน่นอนซึ่งค่า Observable แต่ยัง Serializable.

นี่เห็นได้ชัดว่าทำงานและ serialization ทำงานแต่มันเป็นมาก boilerplate-หนักมากจะต้องเพิ่ม setter สำหรับทุกภาคสนามแล้วด้วยตนเองกำลังจะโทรหา change(...) ในแต่ละ setter:

interface Observable {

    fun change(message: String) {
        println("changing $message")
    }
}

@Serializable
class BlahVO : Observable {

    var value2: String = ""
        set(value) {
            field = value
            change("value2")
        }

    fun toJson(): String {
        return Json.encodeToString(serializer(), this)
    }
}

println(BlahVO().apply { value2 = "test2" }) อย่างถูกต้องแสดงผล

changing value2
{"value2":"test2"}

ฉันพยายามแนะนำ Delegates:

interface Observable {

    fun change(message: String) {
        println("changing $message")
    }

    
    @Suppress("ClassName")
    class default<T>(defaultValue: T) {

        private var value: T = defaultValue

        operator fun getValue(observable: Observable, property: KProperty<*>): T {
            return value
        }

        operator fun setValue(observable: Observable, property: KProperty<*>, value: T) {
            this.value = value
            observable.change(property.name)
        }

    }

}

@Serializable
class BlahVO : Observable {

    var value1: String by Observable.default("value1")

    fun toJson(): String {
        return Json.encodeToString(serializer(), this)
    }
}

println(BlahVO().apply { value1 = "test1" }) อย่างถูกต้องเปลี่ยนตัวกระตุ้นการตรวจสอบ,แต่มันไม่ serialize:

changing value1
{}

ถ้าฉันไปจาก Observable จะ ReadWriteProperty,

interface Observable {

    fun change(message: String) {
        println("changing $message")
    }

    fun <T> look(defaultValue: T): ReadWriteProperty<Observable, T> {
        return OP(defaultValue, this)
    }

    class OP<T>(defaultValue: T, val observable: Observable) : ObservableProperty<T>(defaultValue) {
        override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
            super.setValue(thisRef, property, value)
            observable.change("blah!")
        }
    }
}

@Serializable
class BlahVO : Observable {

    var value3: String by this.look("value3")

    fun toJson(): String {
        return Json.encodeToString(serializer(), this)
    }
}

ผลคือคนเดียวกัน:

changing blah!
{}

เหมือนๆกันสำหรับ Delegates.vetoable

var value4: String by Delegates.vetoable("value4", {
        property: KProperty<*>, oldstring: String, newString: String ->
    this.change(property.name)
    true
})

แสดงผล:

changing value4
{}

Delegates แค่ไม่ดูเหมือนจะทำงานกับ Kotlin Serialization

สิ่งที่ตัวเลือกอื่นๆจะไปที่นั่นเพื่อสังเกตการณ์เป็นทรัพย์สินเปลี่ยนแปลงได้โดยไม่ผิดของมัน serialization ว่าจะยังทำงานอีก platforms(KotlinJS,KotlinJVM,Android,...)?

1

คำตอบที่ดีที่สุด

2

Serialization และ Deserialization ของ Kotlin Delegates คือไม่ได้รับการสนับสนุนจาก kotlinx.serialization ตอนนี้.
นั่นคือการ เปิดปัญหา#1578 บ GitHub เกี่ยวกับคุณสมบัตินี้.

ตามประเด็นคุณสามารถสร้างซีข้อมูล-โอนย้ายวัตถุที่ได้ serialized แทนที่จะเป็นดั้งเดิมเดาใจ ก็คุณสามารถเขียนเป็นกำหนดเอง comment นเพื่อช่วยเหลือพวก serialization ของ Kotlin Delegates ซึ่งดูเหมือนจะเป็นยิ่งกว่า boilerplate งั้นเขียนกำหนดเองรัและ setters ที่ขอเธอแต่งงานในคำถาม


การโอนถ่ายข้อมูลวัตถุ

โดยวางแผนดั้งเดิมของคุณวัตถุไปเป็นง่ายๆการโอนถ่ายข้อมูลได้โดยไม่ delegates คุณสามารถใช้ค่าปริยาย serialization mechanisms. มันก็ยังคงมีอดีผมเป็นผลข้างเคียงที่จะกวาดล้างของคุณข้อมูลของนางแบบชั้นเรียนอกจากส่วนเฟรมเวิร์กเจาะจงหมายเหตุประกอบอย่างเช่น @Serializable.

class DataModel {
    var observedProperty: String by Delegates.observable("initial") { property, before, after ->
        println("""Hey, I changed "${property.name}" from "$before" to "$after"!""")
    }

    fun toJson(): String {
        return Json.encodeToString(serializer(), this.toDto())
    }
}

fun DataModel.toDto() = DataTransferObject(observedProperty)

@Serializable
class DataTransferObject(val observedProperty: String)

fun main() {
    val data = DataModel()
    println(data.toJson())
    data.observedProperty = "changed"
    println(data.toJson())
}

นี้มีค่าต่อไปนี้ผล:

{"observedProperty":"initial"}
Hey, I changed "observedProperty" from "initial" to "changed"!
{"observedProperty":"changed"}

กำหนดเองข้อมูลประเภท

ถ้าเปลี่ยนข้อมูลประเภทคือเป็นตัวเลือกนี้คุณสามารถเขียนเป็นห่อเรียนซึ่งถู(de)serialized transparently. บางอย่างตามครรลองจากเกิดข้อผิดพลาดต่อไปนี้อาจจะทำงานแล้ว

@Serializable
class ClassWithMonitoredString(val monitoredProperty: MonitoredString) {
    fun toJson(): String {
        return Json.encodeToString(serializer(), this)
    }
}

fun main() {
    val monitoredString = obs("obsDefault") { before, after ->
        println("""I changed from "$before" to "$after"!""")
    }
    
    val data = ClassWithMonitoredString(monitoredString)
    println(data.toJson())
    data.monitoredProperty.value = "obsChanged"
    println(data.toJson())
}

ซึ่งมีค่าต่อไปนี้ผล:

{"monitoredProperty":"obsDefault"}
I changed from "obsDefault" to "obsChanged"!
{"monitoredProperty":"obsChanged"}

คุณอย่างไรก็ตามสูญเสียข้อมูลเกี่ยวกับซึ่งทรัพย์สินเปลี่ยนไปอย่างที่เจ้าไม่มีเรื่องง่ายใช้งานภาคสนามชื่อนะ ก็คุณต้องเปลี่ยนข้อมูลของสิ่งก่อนสร้างอย่างที่พูดถึงอยู่เหนือและอาจจะไม่ใช่ค desirable หรือแม้กระทั่งจะเป็นไปได้ ที่เพิ่มมานอกเหนือจากนี้ทำงานเพียงสำหรับอิทธิพลเพื่อช่วยตอนนี้ถึงแม้คนนึงอาจทำให้มันมากกทั่วไปนะ แล้วก็นี้ต้องการมากมาย boilerplate จะเริ่มด้วย ที่เรียกเว็บไซต์อย่างไรก็ตามคุณแค่ต้องห่มจริงค่าในข้อเรียกต้อง obs. ฉันเคยต่อไปนี้ boilerplate จะเอามันไปทำงานแล้ว

typealias OnChange = (before: String, after: String) -> Unit

@Serializable(with = MonitoredStringSerializer::class)
class MonitoredString(initialValue: String, var onChange: OnChange?) {
    var value: String = initialValue
        set(value) {
            onChange?.invoke(field, value)

            field = value
        }

}

fun obs(value: String, onChange: OnChange? = null) = MonitoredString(value, onChange)

object MonitoredStringSerializer : KSerializer<MonitoredString> {
    override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("MonitoredString", PrimitiveKind.STRING)

    override fun serialize(encoder: Encoder, value: MonitoredString) {
        encoder.encodeString(value.value)
    }

    override fun deserialize(decoder: Decoder): MonitoredString {
        return MonitoredString(decoder.decodeString(), null)
    }
}
2021-11-24 18:19:41

ฉันกำลังเป็นไปตามที่คล้ายกันมากครับแต่มันรู้สึกเหมือนมันจะดีขึ้น ฉันคงต้องเป็นขั้นต่อไปสร้างวิธีการ monitoredString ซึ่งจะได้ค่าเป็น MonitoredString และเนื่องจากฟังก์ชันที่ใช้เรื่องนี้ฉันไม่ต้องผ่านกา onChange ฉันสามารถเชื่อมโยงมันต้อง OnChange จากเรื่องนี้ ข้อแย่ของการมี Observable"ของรัฐ"ชั้นเรียนและจากนั้นก็ถ่ายโอนข้อมูลชั้นเรียนที่สามารถเป็น serialized เป็น duplication ของรุ่นของฟิลด์ ดูเหมือนคนเดียวดีทางนั้น achieves สิ่งที่ฉันต้องการจะทำคือการ annotate กับ@บางอย่างแล้วก็สร้างค boilerplate ใช้ KSP.
Jan Vladimir Mostert

ในภาษาอื่นๆ

หน้านี้อยู่ในภาษาอื่นๆ

Русский
..................................................................................................................
Italiano
..................................................................................................................
Polski
..................................................................................................................
Română
..................................................................................................................
한국어
..................................................................................................................
हिन्दी
..................................................................................................................
Français
..................................................................................................................
Türk
..................................................................................................................
Česk
..................................................................................................................
Português
..................................................................................................................
中文
..................................................................................................................
Español
..................................................................................................................
Slovenský
..................................................................................................................

ดังอยู่ในนี้หมวดหมู่

ดังคำถามอยู่ในนี้หมวดหมู่