Rumah >Java >javaTutorial >Melaksanakan Campuran (atau Sifat) dalam Kotlin Menggunakan Delegasi
(Baca artikel ini dalam bahasa Perancis di laman web saya)
Dalam pengaturcaraan berorientasikan objek, Mixin ialah cara untuk menambah satu atau lebih fungsi yang dipratentukan dan autonomi pada kelas. Sesetengah bahasa menyediakan keupayaan ini secara langsung, manakala yang lain memerlukan lebih banyak usaha dan berkompromi untuk kod Mixin. Dalam artikel ini, saya menerangkan pelaksanaan Mixin dalam Kotlin menggunakan perwakilan.
Corak mixin tidak ditakrifkan dengan tepat seperti corak reka bentuk lain seperti Singleton atau Proxy. Bergantung pada konteks, mungkin terdapat sedikit perbezaan dalam maksud istilah tersebut.
Corak ini juga boleh hampir dengan "Sifat" yang terdapat dalam bahasa lain (cth., Karat), tetapi begitu juga, istilah "Sifat" tidak semestinya bermaksud perkara yang sama bergantung pada bahasa yang digunakan1.
Yang berkata, berikut ialah definisi daripada Wikipedia:
Dalam pengaturcaraan berorientasikan objek, mixin (atau mix-in) ialah kelas yang mengandungi kaedah yang digunakan oleh kelas lain tanpa perlu menjadi kelas induk bagi kelas lain tersebut. Cara kelas lain ini mengakses kaedah mixin bergantung pada bahasa. Mixin kadangkala digambarkan sebagai "disertakan" dan bukannya "diwarisi."
Anda juga boleh mencari definisi dalam pelbagai artikel mengenai subjek pengaturcaraan berasaskan mixin (2, 3, 4). Takrifan ini juga membawa tanggapan lanjutan kelas ini tanpa hubungan ibu bapa-anak (atau is-a) yang disediakan oleh warisan klasik. Ia selanjutnya dikaitkan dengan berbilang warisan, yang tidak mungkin di Kotlin (atau di Jawa) tetapi dibentangkan sebagai salah satu kepentingan menggunakan campuran.
Pelaksanaan corak yang hampir sepadan dengan takrifan ini mesti memenuhi kekangan berikut:
Cara paling remeh untuk menambah fungsi pada kelas ialah menggunakan kelas lain sebagai atribut. Kefungsian mixin kemudiannya boleh diakses dengan memanggil kaedah atribut ini.
class MyClass { private val mixin = Counter() fun myFunction() { mixin.increment() // ... } }
Kaedah ini tidak memberikan sebarang maklumat kepada sistem jenis Kotlin. Sebagai contoh, adalah mustahil untuk mempunyai senarai objek menggunakan Counter. Mengambil objek jenis Counter sebagai parameter tidak mempunyai kepentingan kerana jenis ini hanya mewakili mixin dan dengan itu objek mungkin tidak berguna untuk seluruh aplikasi.
Masalah lain dengan pelaksanaan ini ialah kefungsian mixin tidak boleh diakses dari luar kelas tanpa mengubah suai kelas ini atau membuat mixin awam.
Untuk mixin juga mentakrifkan jenis yang boleh digunakan dalam aplikasi, kita perlu mewarisi daripada kelas abstrak atau melaksanakan antara muka.
Menggunakan kelas abstrak untuk mentakrifkan mixin adalah di luar persoalan, kerana ia tidak membenarkan kami menggunakan berbilang campuran pada satu kelas (tidak mungkin untuk mewarisi daripada berbilang kelas dalam Kotlin).
Sehingga campuran akan dibuat dengan antara muka.
interface Counter { var count: Int fun increment() { println("Mixin does its job") } fun get(): Int = count } class MyClass: Counter { override var count: Int = 0 // We are forced to add the mixin's state to the class using it fun hello() { println("Class does something") } }
Pendekatan ini lebih memuaskan daripada yang sebelumnya atas beberapa sebab:
Walau bagaimanapun, masih terdapat had yang ketara untuk pelaksanaan ini: campuran tidak boleh mengandungi keadaan. Malah, walaupun antara muka dalam Kotlin boleh menentukan sifat, mereka tidak boleh memulakannya secara langsung. Oleh itu, setiap kelas yang menggunakan mixin mesti menentukan semua sifat yang diperlukan untuk operasi mixin. Ini tidak menghormati kekangan bahawa kami tidak mahu penggunaan mixin memaksa kami menambah sifat atau kaedah pada kelas yang menggunakannya.
Oleh itu, kami perlu mencari penyelesaian agar mixin mempunyai keadaan sambil mengekalkan antara muka sebagai satu-satunya cara untuk mempunyai kedua-dua jenis dan keupayaan untuk menggunakan berbilang campuran.
Penyelesaian ini lebih kompleks sedikit untuk mentakrifkan campuran; namun, ia tidak memberi kesan kepada kelas yang menggunakannya. Caranya adalah dengan mengaitkan setiap mixin dengan objek untuk mengandungi keadaan yang mungkin diperlukan oleh mixin. Kami akan menggunakan objek ini dengan mengaitkannya dengan ciri perwakilan Kotlin untuk mencipta objek ini bagi setiap penggunaan mixin.
Inilah penyelesaian asas yang namun memenuhi semua kekangan:
class MyClass { private val mixin = Counter() fun myFunction() { mixin.increment() // ... } }
Kami boleh menambah baik lagi pelaksanaan: kelas CounterHolder ialah perincian pelaksanaan, dan adalah menarik jika tidak perlu mengetahui namanya.
Untuk mencapai ini, kami akan menggunakan objek pasangan pada antara muka mixin dan corak "Kaedah Kilang" untuk mencipta objek yang mengandungi keadaan mixin. Kami juga akan menggunakan sedikit ilmu hitam Kotlin jadi kami tidak perlu mengetahui nama kaedah ini:
interface Counter { var count: Int fun increment() { println("Mixin does its job") } fun get(): Int = count } class MyClass: Counter { override var count: Int = 0 // We are forced to add the mixin's state to the class using it fun hello() { println("Class does something") } }
Pelaksanaan mixin ini tidak sempurna (tidak ada yang sempurna tanpa disokong pada peringkat bahasa, pada pendapat saya). Khususnya, ia menunjukkan kelemahan berikut:
interface Counter { fun increment() fun get(): Int } class CounterHolder: Counter { var count: Int = 0 override fun increment() { count++ } override fun get(): Int = count } class MyClass: Counter by CounterHolder() { fun hello() { increment() // The rest of the method... } }
Jika anda menggunakan ini di dalam mixin, anda merujuk kepada contoh kelas Pemegang.
Untuk meningkatkan pemahaman tentang corak yang saya cadangkan dalam artikel ini, berikut ialah beberapa contoh campuran yang realistik.
Campuran ini membenarkan kelas untuk "merekod" tindakan yang dilakukan pada tika kelas itu. Mixin menyediakan kaedah lain untuk mendapatkan semula acara terkini.
class MyClass { private val mixin = Counter() fun myFunction() { mixin.increment() // ... } }
Corak reka bentuk Observable boleh dilaksanakan dengan mudah menggunakan mixin. Dengan cara ini, kelas boleh diperhatikan tidak lagi perlu mentakrifkan logik langganan dan pemberitahuan, mahupun mengekalkan senarai pemerhati itu sendiri.
interface Counter { var count: Int fun increment() { println("Mixin does its job") } fun get(): Int = count } class MyClass: Counter { override var count: Int = 0 // We are forced to add the mixin's state to the class using it fun hello() { println("Class does something") } }
Terdapat kelemahan dalam kes khusus ini, walau bagaimanapun: kaedah notifyObservers boleh diakses dari luar kelas Katalog, walaupun kami mungkin lebih suka merahsiakannya. Tetapi semua kaedah mixin mestilah awam untuk digunakan daripada kelas menggunakan mixin (kerana kami tidak menggunakan warisan tetapi gubahan, walaupun sintaks yang dipermudahkan oleh Kotlin menjadikannya kelihatan seperti warisan).
Jika projek anda mengurus data perniagaan yang berterusan dan/atau anda berlatih, sekurang-kurangnya sebahagiannya, DDD (Reka Bentuk Didorong Domain), aplikasi anda mungkin mengandungi entiti. Entiti ialah kelas dengan identiti, selalunya dilaksanakan sebagai ID berangka atau UUID. Ciri ini sesuai dengan penggunaan mixin, dan berikut ialah contohnya.
interface Counter { fun increment() fun get(): Int } class CounterHolder: Counter { var count: Int = 0 override fun increment() { count++ } override fun get(): Int = count } class MyClass: Counter by CounterHolder() { fun hello() { increment() // The rest of the method... } }
Contoh ini agak berbeza: kami melihat tiada apa yang menghalang kami daripada menamakan kelas Pemegang secara berbeza dan tiada apa yang menghalang kami daripada menghantar parameter semasa instantiasi.
Teknik mixin membolehkan kelas memperkaya dengan menambahkan gelagat yang sering melintang dan boleh diguna semula tanpa perlu mengubah suai kelas ini untuk menampung fungsi ini. Walaupun terdapat beberapa batasan, mixin membantu memudahkan penggunaan semula kod dan mengasingkan fungsi tertentu yang biasa kepada beberapa kelas dalam aplikasi.
Mixins ialah alat yang menarik dalam kit alat pembangun Kotlin, dan saya menggalakkan anda untuk meneroka kaedah ini dalam kod anda sendiri, sambil menyedari kekangan dan alternatifnya.
Fakta yang menggembirakan: Kotlin mempunyai kata kunci sifat, tetapi kata kunci itu tidak digunakan lagi dan telah digantikan dengan antara muka (lihat https://blog.jetbrains.com/kotlin/2015/05/kotlin-m12-is-out/#traits -adalah-kini-antara muka) ↩
Warisan Berasaskan Campuran ↩
Kelas dan Campuran ↩
Pengaturcaraan Berorientasikan Objek dengan Perisa ↩
Atas ialah kandungan terperinci Melaksanakan Campuran (atau Sifat) dalam Kotlin Menggunakan Delegasi. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!