Rumah >Java >javaTutorial >Melaksanakan Campuran (atau Sifat) dalam Kotlin Menggunakan Delegasi

Melaksanakan Campuran (atau Sifat) dalam Kotlin Menggunakan Delegasi

Mary-Kate Olsen
Mary-Kate Olsenasal
2024-10-18 20:11:031057semak imbas

Implementing Mixins (or Traits) in Kotlin Using Delegation

(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.

  • Objektif
    • Takrifan Corak "Mixins"
    • Ciri dan Kekangan
  • Perlaksanaan
    • Pendekatan Naif mengikut Komposisi
    • Penggunaan Warisan
    • Delegasi untuk Mengandungi Negeri Mixin
    • Pelaksanaan Akhir
  • Penghadan
  • Contoh
    • Boleh diaudit
    • Boleh diperhatikan
    • Entiti / Identiti
  • Kesimpulan

Objektif

Definisi Corak "Mixins".

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.

Ciri dan Kekangan

Pelaksanaan corak yang hampir sepadan dengan takrifan ini mesti memenuhi kekangan berikut:

  • Kami boleh menambah beberapa campuran pada kelas. Kami berada dalam konteks berorientasikan objek, dan jika kekangan ini tidak dipenuhi, corak itu akan mempunyai sedikit minat berbanding dengan kemungkinan reka bentuk lain seperti warisan.
  • Fungsi mixin boleh digunakan dari luar kelas. Begitu juga, jika kita mengabaikan kekangan ini, corak itu tidak akan membawa apa-apa yang tidak dapat kita capai dengan komposisi mudah.
  • Menambah mixin pada kelas tidak memaksa kami untuk menambah atribut dan kaedah dalam definisi kelas. Tanpa kekangan ini, mixin tidak lagi boleh dilihat sebagai fungsi "kotak hitam". Kami bukan sahaja boleh bergantung pada kontrak antara muka mixin untuk menambahkannya ke kelas tetapi perlu memahami fungsinya (cth., melalui dokumentasi). Saya mahu boleh menggunakan mixin kerana saya menggunakan kelas atau fungsi.
  • Mixin boleh mempunyai keadaan. Sesetengah mixin mungkin perlu menyimpan data untuk kefungsian mereka.
  • Mixin boleh digunakan sebagai jenis. Sebagai contoh, saya boleh mempunyai fungsi yang mengambil sebarang objek sebagai parameter selagi ia menggunakan campuran yang diberikan.

Perlaksanaan

Pendekatan Naif mengikut Komposisi

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.

Penggunaan Warisan

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:

  • Kelas yang menggunakan mixin tidak perlu melaksanakan kelakuan mixin terima kasih kepada kaedah lalai
  • Kelas boleh menggunakan berbilang campuran kerana Kotlin membenarkan kelas melaksanakan berbilang antara muka.
  • Setiap mixin mencipta jenis yang boleh digunakan untuk memanipulasi objek berdasarkan mixin yang disertakan oleh kelas mereka.

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.

Delegasi untuk Mengandungi Negeri Mixin

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

        // ...
    }
}

Pelaksanaan Akhir

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

Had

Pelaksanaan mixin ini tidak sempurna (tidak ada yang sempurna tanpa disokong pada peringkat bahasa, pada pendapat saya). Khususnya, ia menunjukkan kelemahan berikut:

  • Semua kaedah mixin mestilah awam. Sesetengah mixin mengandungi kaedah yang bertujuan untuk digunakan oleh kelas menggunakan mixin dan lain-lain yang lebih masuk akal jika dipanggil dari luar. Memandangkan mixin mentakrifkan kaedahnya pada antara muka, adalah mustahil untuk memaksa pengkompil untuk mengesahkan kekangan ini. Kemudian kita mesti bergantung pada dokumentasi atau alat analisis kod statik.
  • Kaedah Mixin tidak mempunyai akses kepada contoh kelas menggunakan mixin. Pada masa pengisytiharan perwakilan, contoh tidak dimulakan dan kami tidak boleh menyerahkannya kepada mixin.
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.

Contoh

Untuk meningkatkan pemahaman tentang corak yang saya cadangkan dalam artikel ini, berikut ialah beberapa contoh campuran yang realistik.

Boleh diaudit

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

        // ...
    }
}

Boleh diperhatikan

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

Entiti / Identiti

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.

Kesimpulan

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.


  1. 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) ↩

  2. Warisan Berasaskan Campuran ↩

  3. Kelas dan Campuran ↩

  4. 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!

Kenyataan:
Kandungan artikel ini disumbangkan secara sukarela oleh netizen, dan hak cipta adalah milik pengarang asal. Laman web ini tidak memikul tanggungjawab undang-undang yang sepadan. Jika anda menemui sebarang kandungan yang disyaki plagiarisme atau pelanggaran, sila hubungi admin@php.cn