Kotlin DSL: Teori dan Praktek

Mengembangkan tes aplikasi bukan pengalaman yang menyenangkan. Proses ini membutuhkan waktu lama, membutuhkan banyak konsentrasi dan sangat diminati. Bahasa Kotlin menyediakan seperangkat alat yang membuatnya cukup mudah untuk membangun bahasa berorientasi masalah Anda sendiri (DSL). Ada pengalaman ketika Kotlin DSL mengganti pembangun dan metode statis untuk menguji modul perencanaan sumber daya, yang membuat menambahkan tes baru dan mendukung yang lama dari rutinitas merupakan proses yang menyenangkan.

Dalam perjalanan artikel, kami akan menganalisis semua alat utama dari gudang pengembang dan bagaimana mereka dapat digabungkan untuk menyelesaikan masalah pengujian. Kami akan berusaha keras mulai dari merancang Tes Ideal hingga meluncurkan tes yang paling mendekati, bersih, dan mudah dipahami untuk sistem perencanaan sumber daya berbasis Kotlin.

Artikel ini akan berguna untuk melatih para insinyur, mereka yang menganggap Kotlin sebagai bahasa untuk menulis tes ringkas dengan nyaman, dan mereka yang ingin meningkatkan proses pengujian dalam proyek mereka.



Artikel ini didasarkan pada presentasi oleh Ivan Osipov ( i_osipov ) di konferensi JPoint. Narasi lebih lanjut dilakukan atas namanya. Ivan bekerja sebagai programmer di Haulmont. Produk utama perusahaan adalah CUBA, sebuah platform untuk mengembangkan perusahaan dan berbagai aplikasi web. Secara khusus, proyek-proyek outsourcing sedang dibuat pada platform ini, di antaranya ada baru-baru ini sebuah proyek di bidang pendidikan, di mana Ivan terlibat dalam membangun jadwal untuk sebuah lembaga pendidikan. Kebetulan selama tiga tahun terakhir Ivan telah bekerja sama dengan para perencana, dan khususnya di Haulmont mereka telah menguji perencana ini selama setahun.

Bagi mereka yang ingin menjalankan contoh - simpan tautan ke GitHub . Di bawah tautan Anda akan menemukan semua kode yang akan kami uraikan, jalankan, dan tulis hari ini. Buka kodenya dan pergi!



Hari ini kita akan membahas:

  • apa bahasa yang berorientasi masalah;
  • built-in bahasa berorientasi masalah;
  • membangun jadwal untuk lembaga pendidikan;
  • bagaimana semuanya diuji dengan Kotlin.

Hari ini saya akan berbicara secara rinci tentang alat-alat yang kami miliki dalam bahasa tersebut, menunjukkan kepada Anda beberapa demo, dan kami akan menulis seluruh tes dari awal hingga akhir. Pada saat yang sama, saya ingin menjadi lebih objektif, jadi saya akan berbicara tentang beberapa kelemahan yang saya identifikasi untuk diri saya sendiri selama pengembangan.

Mari kita mulai dengan berbicara tentang modul pembuatan jadwal. Jadi, pembangunan jadwal berlangsung dalam beberapa tahap. Setiap langkah ini perlu diuji secara terpisah. Anda perlu memahami bahwa meskipun langkah-langkahnya berbeda, kami memiliki model data umum.



Proses ini dapat direpresentasikan sebagai berikut: pada input ada beberapa data dengan model umum, pada output ada jadwal. Data divalidasi, disaring, kemudian dibangun kelompok pelatihan. Ini mengacu pada bidang subjek dari jadwal untuk lembaga pendidikan. Berdasarkan kelompok yang dibangun dan berdasarkan beberapa data lain, kami menempatkan pelajaran. Hari ini kita hanya akan berbicara tentang tahap terakhir - tentang penempatan kelas.



Sedikit tentang menguji penjadwal.

Pertama, seperti yang sudah Anda pahami, tahapan yang berbeda harus diuji secara terpisah. Seseorang dapat memilih proses pengujian pengujian yang kurang lebih standar: ada inisialisasi data, ada peluncuran scheduler, ada cek dari hasil scheduler itu sendiri. Ada sejumlah besar kasus bisnis yang berbeda yang perlu dicakup dan berbagai situasi yang perlu dipertimbangkan sehingga ketika membuat jadwal, situasi ini juga tetap ada.

Model kadang-kadang bisa berbobot, dan untuk membuat satu entitas, perlu menginisialisasi lima entitas tambahan, atau bahkan lebih. Jadi, secara total, sejumlah besar kode diperoleh, yang kami tulis berulang-ulang untuk setiap tes. Dukungan untuk tes semacam itu membutuhkan banyak waktu. Jika Anda ingin memperbarui model, dan ini kadang terjadi, maka skala perubahan memengaruhi tes.

Mari kita menulis tes:



Mari kita menulis tes paling sederhana sehingga Anda secara umum memahami gambarnya.
Apa yang pertama kali terlintas dalam pikiran ketika Anda berpikir tentang pengujian? Mungkin ini adalah beberapa tes primitif semacam ini: Anda membuat kelas, membuat metode di dalamnya, menandainya dengan Tes anotasi. Akibatnya, kami menggunakan kemampuan JUnit, dan menginisialisasi beberapa data, nilai default, lalu nilai tes khusus, melakukan hal yang sama untuk sisa model, dan akhirnya membuat objek scheduler, mentransfer data kami ke sana, kami mulai, kami menerima hasil dan kami memeriksanya. Kurang lebih proses standar. Tapi jelas ada duplikasi kode di dalamnya. Hal pertama yang terlintas dalam pikiran adalah kemampuan untuk memasukkan semuanya ke dalam metode statis. Karena ada banyak nilai default, mengapa tidak menyembunyikannya?



Ini adalah langkah pertama yang baik untuk mengurangi duplikasi.



Melihat ini, Anda mengerti bahwa saya ingin menjaga model lebih kompak. Di sini kita memiliki pola pembangun di mana, di suatu tempat di bawah kap, nilai default diinisialisasi, dan nilai-nilai khusus tes diinisialisasi di sana. Semakin baik, namun, kami masih menulis kode boilerplate, dan kami menulisnya lagi setiap waktu. Bayangkan 200 tes - Anda harus menulis tiga baris ini 200 kali. Jelas, saya ingin menyingkirkan ini entah bagaimana. Mengembangkan ide, kami mencapai batas tertentu. Jadi, misalnya, kita dapat membuat pembuat pola secara umum untuk semuanya.



Anda dapat membuat penjadwal dari awal hingga akhir, mengatur semua nilai yang kita butuhkan, memulai penjadwalan dan semuanya hebat. Jika Anda melihat detail pada contoh ini dan menganalisisnya secara terperinci, ternyata banyak kode yang tidak perlu sedang ditulis. Saya ingin membuat tes lebih mudah dibaca sehingga Anda dapat melihatnya dan segera mengerti, tanpa mempelajari pola dan sebagainya.

Jadi, kami memiliki beberapa kode yang tidak perlu. Matematika sederhana menunjukkan bahwa ada 55% lebih banyak surat daripada yang kita butuhkan, dan saya ingin entah bagaimana menjauh darinya.



Setelah beberapa waktu, dukungan untuk pengujian kami ternyata lebih mahal, karena Anda perlu mendukung lebih banyak kode. Kadang-kadang, jika kita tidak melakukan upaya apa pun, keterbacaan meninggalkan banyak hal yang diinginkan, atau ternyata dapat diterima, tetapi kami ingin lebih baik. Mungkin nanti kita akan mulai menambahkan semacam kerangka kerja, perpustakaan, untuk membuat tes lebih mudah untuk ditulis. Karena ini, kami meningkatkan tingkat masuk ke pengujian aplikasi kami. Di sini kami memiliki aplikasi yang sudah rumit, tingkat masuk ke dalam pengujiannya sangat signifikan, dan kami semakin meningkatkannya.

Tes sempurna


Sangat menyenangkan untuk mengatakan betapa buruknya semuanya, tetapi mari kita pikirkan bagaimana itu akan sangat baik. Contoh ideal yang ingin kami dapatkan sebagai hasilnya:



Bayangkan bahwa ada beberapa deklarasi di mana kita mengatakan bahwa ini adalah tes dengan nama tertentu, dan kami ingin menggunakan spasi untuk memisahkan kata-kata dalam nama, bukan CamelCase. Kami sedang membuat jadwal, kami memiliki beberapa data, dan hasil perencana diperiksa. Karena kami bekerja terutama dengan Java, dan semua kode aplikasi utama ditulis dalam bahasa ini, saya ingin memiliki kemampuan pengujian yang kompatibel. Saya ingin menginisialisasi data sejelas mungkin kepada pembaca. Saya ingin menginisialisasi beberapa data umum dan bagian dari model yang kita butuhkan. Misalnya, buat siswa, guru, dan jelaskan kapan tersedia. Ini adalah contoh sempurna kami.

Bahasa khusus domain




Melihat semuanya, itu mulai tampak seperti semacam bahasa yang berorientasi masalah. Anda perlu memahami apa itu dan apa bedanya. Bahasa dapat dibagi menjadi dua jenis: bahasa tujuan umum (apa yang kami tulis terus-menerus, menyelesaikan semua tugas dan mengatasi semuanya) dan bahasa yang berorientasi masalah. Jadi, misalnya, SQL membantu kita untuk menarik data dari database dengan sempurna, dan beberapa bahasa lain juga membantu menyelesaikan masalah spesifik lainnya.



Salah satu cara untuk mengimplementasikan bahasa berorientasi masalah adalah bahasa yang disematkan, atau internal. Bahasa-bahasa tersebut diimplementasikan berdasarkan bahasa tujuan umum. Yaitu, beberapa konstruksi bahasa tujuan umum kami membentuk sesuatu seperti basis - itulah yang kami gunakan ketika bekerja dengan bahasa yang berorientasi masalah. Dalam hal ini, tentu saja, peluang muncul dalam bahasa yang berorientasi masalah untuk menggunakan semua fitur dan fitur yang berasal dari bahasa tujuan umum.



Sekali lagi, lihat contoh sempurna kami dan pikirkan bahasa mana yang harus dipilih. Kami memiliki tiga opsi.



Opsi pertama adalah Groovy. Bahasa yang indah dan dinamis yang telah membuktikan dirinya dalam membangun bahasa yang berorientasi masalah. Sekali lagi, Anda dapat memberikan contoh file build di Gradle, yang banyak dari kita gunakan. Ada juga Scala, yang memiliki sejumlah besar peluang untuk implementasi sesuatu mereka sendiri. Dan akhirnya, ada Kotlin, yang juga membantu kami membangun bahasa yang berorientasi masalah, dan hari ini akan dibahas. Saya tidak ingin mengembangbiakkan perang dan membandingkan Kotlin dengan sesuatu yang lain, tetapi tetap pada hati nurani Anda. Hari ini saya akan menunjukkan kepada Anda apa yang dimiliki Kotlin untuk mengembangkan bahasa yang berorientasi masalah. Ketika Anda ingin membandingkan ini dan mengatakan bahwa beberapa bahasa lebih baik, Anda dapat kembali ke artikel ini dan dengan mudah melihat perbedaannya.



Apa yang Kotlin berikan kepada kita untuk mengembangkan bahasa yang berorientasi masalah?

Pertama, ini adalah pengetikan statis, dan semua yang terjadi kemudian. Pada tahap kompilasi, sejumlah besar masalah terdeteksi, dan ini sangat menghemat, terutama dalam kasus ketika Anda tidak ingin mendapatkan masalah yang terkait dengan sintaks dan penulisan dalam tes.
Lalu, ada sistem inferensi tipe besar yang berasal dari Kotlin. Ini luar biasa, karena tidak perlu menulis jenis apa pun lagi dan lagi, semuanya ditampilkan oleh kompiler dengan keras.

Ketiga, ada dukungan yang sangat baik untuk lingkungan pengembangan, dan ini tidak mengejutkan, karena perusahaan yang sama membuat lingkungan pengembangan utama untuk hari ini, dan itu memang Kotlin.
Akhirnya di dalam DSL, jelas kita bisa menggunakan Kotlin. Menurut pendapat subjektif saya, mendukung DSL jauh lebih mudah daripada mendukung kelas utilitas. Seperti yang akan Anda lihat nanti, keterbacaan sedikit lebih baik daripada pembangun. Yang saya maksud dengan "lebih baik": Anda mendapatkan sedikit sintaksis yang perlu Anda tulis - seseorang yang membaca bahasa Anda yang berorientasi masalah akan lebih cepat melakukannya. Akhirnya, menulis sepeda Anda jauh lebih menyenangkan! Tetapi pada kenyataannya, menerapkan bahasa yang berorientasi masalah jauh lebih mudah daripada mempelajari beberapa kerangka kerja baru.

Saya akan mengingatkan sekali lagi tautan ke GitHub , jika Anda ingin menulis demo lebih lanjut, maka Anda dapat masuk dan mengambil kode dari tautan tersebut.

Merancang yang ideal di Kotlin


Mari kita lanjutkan merancang cita-cita kita, tetapi sudah ada di Kotlin. Lihatlah contoh kami:



Dan secara bertahap kita akan mulai membangunnya kembali.

Kami memiliki tes yang berubah menjadi fungsi di Kotlin, yang dapat dinamai menggunakan spasi.



Kami akan menandainya dengan anotasi Tes , yang tersedia bagi kami dari JUnit. Di Kotlin, Anda bisa menggunakan formulir singkat untuk fungsi penulisan dan, melalui =, singkirkan kurung kurawal ekstra untuk fungsi itu sendiri.

Jadwal kami berubah menjadi blok. Hal yang sama terjadi dengan banyak desain, karena kami masih bekerja di Kotlin.



Mari kita beralih ke yang lain. Kurung kurawal muncul lagi, kami tidak akan menyingkirkannya, tetapi setidaknya mencoba untuk lebih dekat dengan contoh kita. Dengan membuat konstruksi dengan spasi, kami dapat memperbaiki diri dan membuatnya berbeda, tetapi bagi saya tampaknya lebih baik membuat metode biasa yang akan merangkum pemrosesan, tetapi secara umum hal ini akan jelas bagi pengguna .



Siswa kami berubah menjadi blok tempat kami bekerja dengan properti, dengan metode, dan kami akan terus menganalisis ini dengan Anda.



Akhirnya, para guru. Di sini kita memiliki beberapa blok bersarang.



Dalam kode di bawah ini, kami beralih ke cek. Kami perlu memeriksa kompatibilitas dengan bahasa Java - dan ya, Kotlin kompatibel dengan Java.



Arsenal pengembangan DSL di Kotlin




Mari kita beralih ke daftar alat yang kita miliki. Di sini saya membawa tablet, mungkin daftar semua yang diperlukan untuk mengembangkan bahasa yang berorientasi masalah di Kotlin. Anda dapat kembali kepadanya dari waktu ke waktu dan menyegarkan ingatannya.

Tabel menunjukkan beberapa perbandingan sintaksis berorientasi masalah dan sintaksis biasa yang tersedia dalam bahasa.

Lambdas di Kotlin


val lambda: () -> Unit = { }

Mari kita mulai dengan batu bata paling dasar yang kita miliki di Kotlin - ini adalah lambdas.
Hari ini, berdasarkan tipe lambda, maksud saya hanya tipe fungsional. Lambdas dilambangkan sebagai berikut: ( ) -> .

Kami menginisialisasi lambda dengan bantuan kurung kurawal, di dalamnya kita dapat menulis beberapa kode yang akan dipanggil. Yaitu, lambda, sebenarnya, menyembunyikan kode ini sendiri. Menjalankan lambda seperti itu tampak seperti pemanggilan fungsi, hanya tanda kurung.



Jika kita ingin melewatkan beberapa jenis parameter, pertama, kita harus menggambarkannya dalam tipe.
Kedua, kita memiliki akses ke pengenal default itu, yang dapat kita gunakan, namun, jika ini tidak cocok untuk kita, kita dapat mengatur nama parameter kita sendiri dan menggunakannya.



Pada saat yang sama, kita dapat melewati penggunaan parameter ini dan menggunakan garis bawah agar tidak menghasilkan pengidentifikasi. Dalam hal ini, untuk mengabaikan pengenal, akan mungkin untuk menulis apa-apa, tetapi dalam kasus umum untuk beberapa parameter ada "_" yang disebutkan.



Jika kita ingin melewatkan lebih dari satu parameter, kita perlu mendefinisikan pengidentifikasi mereka secara eksplisit.



Akhirnya, apa yang akan terjadi jika kita mencoba mengoper lambda ke beberapa fungsi dan menjalankannya di sana. Itu terlihat dalam perkiraan awal sebagai berikut: kita memiliki fungsi yang kita lewati lambda dalam kurung keriting, dan jika di Kotlin lambda ditulis sebagai parameter terakhir, kita dapat mengurutkannya dari kurung ini.



Jika tidak ada yang tersisa di tanda kurung, kita dapat menghapus tanda kurung. Mereka yang akrab dengan Groovy harus terbiasa dengan ini.



Di mana ini berlaku? Di mana-mana. Artinya, kawat gigi yang sangat keriting yang telah kita bicarakan, kita gunakan, ini adalah sangat lambda.



Sekarang mari kita lihat salah satu varietas lambda, saya menyebutnya lambda dengan konteks. Anda akan menemukan beberapa nama lain, misalnya, lambda dengan penerima, dan mereka berbeda dari lambda biasa ketika mendeklarasikan tipe sebagai berikut: di sebelah kiri, kami menambahkan beberapa kelas konteks, itu bisa berupa kelas apa saja.



Untuk apa ini? Ini diperlukan agar di dalam lambda kita memiliki akses ke kata kunci ini - ini adalah kata kunci itu sendiri, itu memberi tahu kita konteks kita, yaitu, ke beberapa objek yang kita tautkan ke lambda kita. Jadi, misalnya, kita dapat membuat lambda yang akan menampilkan beberapa string, secara alami, kita akan menggunakan kelas string untuk mendeklarasikan konteks dan panggilan lambda akan terlihat seperti ini:







Jika Anda ingin melewatkan konteks sebagai parameter, Anda bisa melakukannya juga. Namun, kita tidak dapat sepenuhnya menyampaikan konteksnya, yaitu, lambda dengan konteks membutuhkan perhatian! - konteks, ya. Apa yang terjadi jika kita mulai memberikan lambda dengan konteks ke beberapa metode? Di sini kita melihat lagi pada metode exec kami:



Ganti nama menjadi metode siswa - tidak ada yang berubah:



Jadi kami secara bertahap pindah ke konstruksi kami, konstruksi siswa, yang di bawah kawat gigi menyembunyikan semua inisialisasi.



Mari kita cari tahu. Kami memiliki semacam fungsi siswa yang mengambil lambda dengan konteks Siswa.



Jelas, kita butuh konteks.



Di sini kita membuat objek dan menjalankan lambda ini di atasnya.



Sebagai hasilnya, kami juga dapat menginisialisasi beberapa nilai default sebelum meluncurkan lambda, jadi kami merangkum semua yang kami butuhkan untuk fungsi tersebut.



Karena ini, di dalam lambda, kami mendapatkan akses ke kata kunci ini - itu sebabnya, mungkin, ada lambda dengan konteks.



Secara alami, kami dapat menyingkirkan kata kunci ini dan kami mendapat kesempatan untuk menulis konstruksi semacam itu.



Sekali lagi, jika kita tidak hanya memiliki hak milik, tetapi juga beberapa metode, kita juga dapat memanggil mereka, itu terlihat sangat alami.



Aplikasi


Semua lambda dalam kode ini adalah lambda konteks. Ada sejumlah besar konteks, mereka berpotongan dalam satu dan lain cara dan memungkinkan kita untuk membangun bahasa berorientasi masalah kita.



Meringkas lambda - kita memiliki lambda biasa, ada dengan konteksnya, dan itu dan yang lain dapat digunakan.



Operator


Kotlin memiliki seperangkat operator terbatas yang dapat Anda ganti menggunakan konvensi dan kata kunci operator.

Mari kita lihat guru dan aksesibilitasnya. Misalkan kita mengatakan bahwa guru bekerja pada hari Senin dari jam 8 pagi selama 1 jam. Kami juga ingin mengatakan bahwa, selain satu jam ini, ia bekerja mulai pukul 13.00 selama 1 jam. Saya ingin menyatakan ini menggunakan operator + . Bagaimana ini bisa dilakukan?



Ada beberapa metode ketersediaan yang menerima lambda dengan konteks AvailabilityTable . Ini berarti bahwa ada beberapa kelas yang disebut itu, dan metode Senin dideklarasikan di kelas ini. Metode ini mengembalikan DayPointer sejak Anda perlu melampirkan operator kami ke sesuatu.



Mari kita cari tahu apa itu DayPointer. Ini adalah penunjuk ke tabel ketersediaan beberapa guru, dan harinya sesuai dengan jadwalnya. Kami juga memiliki fungsi waktu yang entah bagaimana akan mengubah beberapa baris menjadi indeks integer: di Kotlin kami memiliki kelas IntRange untuk ini.

Di sebelah kiri adalah DayPointer , di sebelah kanan adalah waktu, dan kami ingin menggabungkannya dengan operator + . Untuk melakukan ini, Anda bisa membuat operator kami di kelas DayPointer . Ini akan mengambil serangkaian nilai tipe Int dan mengembalikan DayPointer sehingga kami dapat DayPointer DSL kami berulang kali.
Sekarang, mari kita lihat desain kunci yang dengannya semuanya dimulai, yang dengannya DSL kita mulai. Implementasinya sedikit berbeda, dan sekarang kita akan mengetahuinya.
Kotlin memiliki konsep tunggal yang dibangun langsung ke dalam bahasa. Untuk melakukan ini, alih-alih kata kunci kelas, kata kunci object digunakan. Jika kita membuat metode di dalam singleton, maka kita dapat mengaksesnya sedemikian rupa sehingga tidak perlu membuat instance dari kelas ini lagi. Kami cukup menyebutnya sebagai metode statis di kelas.



Jika Anda melihat hasil dekompilasi (yaitu, di lingkungan pengembangan, klik Tools -> Kotlin -> Show Kotlin Bytecode -> Decompile), Anda dapat melihat implementasi tunggal berikut:



Ini hanya kelas biasa, dan tidak ada supranatural yang terjadi di sini.
Alat lain yang menarik adalah pernyataan invoke . Bayangkan kita memiliki beberapa kelas A, kita memiliki instance-nya, dan kita ingin menjalankan instance ini, yaitu, memanggil tanda kurung pada objek kelas ini, dan kita dapat melakukan ini berkat operator yang invoke .



Bahkan, tanda kurung memungkinkan kita memanggil metode invoke dan memiliki pengubah operator. Jika kita memberikan lambda dengan konteks ke operator ini, maka kita mendapatkan konstruksi seperti itu.



Membuat instance setiap waktu adalah aktivitas lain, sehingga kami dapat menggabungkan pengetahuan sebelumnya dan saat ini.

Mari kita buat singleton, sebut saja jadwalnya, di dalamnya kita akan mendeklarasikan operator yang dipanggil, di dalamnya kita akan membuat konteks, dan itu akan menerima lambda dengan konteks yang kita buat di sini. Ternyata titik masuk tunggal ke DSL kami, dan, sebagai hasilnya, kami mendapatkan konstruksi yang sama - jadwal dengan kurung kurawal.



Ya, kami sudah membicarakan jadwal, mari kita lihat cek kami.
Kami memiliki guru, kami telah membangun semacam jadwal, dan kami ingin memeriksa bahwa dalam jadwal guru ini pada hari tertentu dalam pelajaran tertentu ada beberapa objek yang akan kami gunakan.



Saya ingin menggunakan tanda kurung siku dan mengakses jadwal kami dengan cara yang secara visual terlihat seperti akses ke array.



Ini dapat dilakukan dengan menggunakan operator: get / set:



Di sini kita tidak melakukan sesuatu yang baru, cukup ikuti konvensi. Dalam hal operator yang ditetapkan, kita perlu memberikan nilai-nilai tambahan ke metode kita:



Jadi, tanda kurung siku untuk membaca berubah menjadi, dan tanda kurung siku tempat kita menetapkan berubah menjadi set.

Demo: objek, operator


Anda dapat membaca teks lebih lanjut atau menonton video di sini . Video memiliki waktu mulai yang jelas, tetapi tidak ada waktu akhir yang ditentukan - pada prinsipnya, setelah dimulai, Anda dapat menontonnya sebelum akhir artikel.

Untuk kenyamanan, saya akan menguraikan esensi video secara langsung dalam teks.

Mari kita menulis ujian. Kami memiliki beberapa objek jadwal, dan jika kami pergi ke implementasinya melalui ctrl + b, maka kita akan melihat semua yang saya bicarakan sebelumnya.



Di dalam objek jadwal, kami ingin menginisialisasi data, lalu melakukan beberapa pemeriksaan, dan di dalam data, kami ingin mengatakan bahwa:

  • sekolah kami buka mulai jam 8 pagi;
  • ada satu set item tertentu yang akan kami buat jadwal;
  • ada beberapa guru yang menggambarkan semacam aksesibilitas;
  • punya siswa;
  • pada prinsipnya, untuk seorang siswa kita hanya perlu mengatakan bahwa dia sedang mempelajari mata pelajaran tertentu.



Dan di sini salah satu kelemahan Kotlin dan bahasa yang berorientasi masalah dimanifestasikan pada prinsipnya: cukup sulit untuk mengatasi beberapa objek yang kita buat sebelumnya. Dalam demo ini, saya akan menunjukkan semuanya sebagai indeks, yaitu, rus adalah indeks 0, matematika adalah indeks 2. Dan guru secara alami juga memimpin sesuatu. Dia tidak hanya pergi bekerja, tetapi terlibat dalam sesuatu. Untuk pembaca artikel ini, saya ingin menawarkan satu opsi lagi untuk pengalamatan, Anda dapat membuat tag unik dan menyimpan entitas pada mereka di Peta, dan ketika Anda perlu mengakses salah satunya, Anda selalu dapat menemukannya dengan tag. Terus membongkar DSL.

Di sini, apa yang harus diperhatikan: pertama, kita memiliki operator +, yang untuk implementasinya kita juga dapat pergi dan melihat bahwa kita sebenarnya memiliki kelas DayPointer, yang membantu kita untuk mengikat semua ini dengan bantuan operator.

Dan berkat kenyataan bahwa kami memiliki akses ke konteks, lingkungan pengembangan memberi tahu kami bahwa dalam konteks kami melalui kata kunci ini, kami memiliki akses ke beberapa koleksi, dan kami akan menggunakannya.



Artinya, kami memiliki koleksi acara. Acara ini merangkum serangkaian properti, misalnya: bahwa ada seorang siswa, seorang guru, hari apa mereka bertemu pada pelajaran apa.



Kami terus menulis ujian lebih lanjut.



Di sini, sekali lagi, kami menggunakan operator get, tidak mudah untuk mencapai implementasinya, tetapi kami bisa melakukannya.



Bahkan, kami hanya mengikuti perjanjian, jadi kami mendapatkan akses ke desain ini.
Mari kita kembali ke presentasi dan melanjutkan pembicaraan tentang Kotlin. Kami ingin cek diterapkan di Kotlin, dan kami melewati acara-acara ini:



Suatu peristiwa pada dasarnya adalah kumpulan 4 properti yang dienkapsulasi. Saya ingin menguraikan peristiwa ini menjadi serangkaian properti, seperti tuple. Di Rusia, konstruksi semacam itu disebut multi-deklarasi (saya hanya menemukan terjemahan semacam itu), atau deklarasi penataan , dan berfungsi sebagai berikut:



Jika salah satu dari Anda tidak terbiasa dengan fitur ini, itu berfungsi seperti ini: Anda dapat mengambil acara, dan di tempat di mana ia digunakan, menggunakan tanda kurung, menguraikannya menjadi seperangkat properti.



Ini berfungsi karena kita memiliki metode componentN, yaitu, itu adalah metode yang dihasilkan oleh kompiler berkat pengubah data yang kita tulis sebelum kelas.



Seiring dengan ini, sejumlah besar metode lain terbang ke kami. Kami tertarik pada metode componentN, yang dihasilkan berdasarkan pada properti yang tercantum dalam daftar parameter konstruktor utama.



Jika kami tidak memiliki pengubah data, akan perlu untuk menulis secara manual operator yang akan melakukan hal yang sama.





Jadi, kami memiliki beberapa metode componentN, dan mereka terurai menjadi panggilan seperti itu:



Intinya, itu adalah gula sintaksis atas panggilan beberapa metode.

Kami telah berbicara tentang beberapa tabel ketersediaan, dan, pada kenyataannya, saya menipu Anda. Itu terjadi. Tidak ada avaiabilityTable , tidak ada di alam, tetapi ada matriks nilai Boolean.



Tidak diperlukan kelas tambahan: Anda bisa mengambil matriks nilai Boolean dan menamainya ulang untuk lebih jelasnya. Ini dapat dilakukan dengan menggunakan apa yang disebut typealias atau tipe alias . Sayangnya, kami tidak mendapatkan bonus tambahan dari ini, itu hanya penamaan ulang. Jika Anda mengambil ketersediaan dan menamainya kembali ke matriks nilai Boolean, tidak ada yang akan berubah sama sekali. Kedua kode tersebut berfungsi dan akan berfungsi.

Mari kita lihat ke guru, ini aksesibilitas yang tepat ini, dan bicarakan tentang dia:



Kami memiliki guru, dan metode ketersediaannya disebut (apakah Anda belum kehilangan alur penalarannya? :-). Dari mana asalnya? Artinya, seorang guru adalah semacam entitas yang memiliki kelas, dan ini adalah kode bisnis. Dan tidak ada metode tambahan.



Metode ini muncul karena fungsi ekstensi. Kita ambil dan kencangkan ke kelas kita beberapa fungsi lain yang bisa kita jalankan pada objek kelas ini.
Jika kita melewatkan beberapa lambda ke fungsi ini, dan kemudian menjalankannya di properti yang ada, maka semuanya baik-baik saja - metode ketersediaan dalam implementasinya menginisialisasi properti ketersediaan. Anda dapat menyingkirkan ini. Kita sudah tahu tentang operator yang dipanggil, yang dapat dilampirkan ke suatu jenis, dan pada saat yang sama menjadi fungsi ekstensi. Jika Anda mengirimkan lambda ke operator ini, maka di sana, dengan kata kunci ini, kita dapat menjalankan lambda ini. Akibatnya, ketika kami bekerja dengan seorang guru, aksesibilitas adalah milik guru, dan bukan metode tambahan, dan tidak ada rassynchron yang terjadi di sini.



Sebagai bonus, fungsi ekstensi dapat dibuat untuk jenis yang dapat dibatalkan. Ini bagus, karena jika ada variabel dengan tipe nullable yang berisi nilai null, fungsi kami sudah siap untuk ini, dan tidak akan jatuh dari NullPointer. Di dalam fungsi ini, ini bisa menjadi nol, dan ini perlu ditangani.



Meringkas fungsi ekstensi: Anda perlu memahami bahwa hanya ada akses ke API publik dari kelas, dan kelas itu sendiri tidak dimodifikasi dengan cara apa pun. Fungsi ekstensi ditentukan oleh tipe variabel, dan bukan oleh tipe aktual. Selain itu, anggota kelas dengan tanda tangan yang sama akan diprioritaskan. Anda dapat membuat fungsi ekstensi untuk satu kelas, tetapi menulisnya di kelas yang sama sekali berbeda, dan di dalam fungsi ekstensi ini akan ada akses ke dua konteks secara bersamaan. Ternyata persimpangan konteks. Dan akhirnya, ini adalah peluang besar untuk membawa dan mengencangkan operator secara umum ke tempat mana pun yang kita inginkan.



Alat selanjutnya adalah fungsi infiks. Palu lain yang berbahaya di tangan pengembang. Kenapa berbahaya? Apa yang Anda lihat adalah kode. Kode semacam itu dapat ditulis di Kotlin, dan jangan lakukan itu! Tolong jangan lakukan itu. Namun demikian, pendekatannya bagus. Berkat ini, dimungkinkan untuk menghilangkan titik-titik, tanda kurung - dari semua sintaks yang berisik, dari mana kami mencoba untuk mendapatkan sejauh mungkin dan membuat kode kami sedikit lebih bersih.



Bagaimana cara kerjanya? Mari kita ambil contoh sederhana - variabel integer. Mari kita buat fungsi ekstensi untuknya, sebut saja shouldBeEqual, itu akan melakukan sesuatu, tapi ini tidak menarik. Jika kami menambahkan pengubah infiks ke kiri, itu sudah cukup. Anda dapat menghilangkan titik dan tanda kurung, tetapi ada beberapa nuansa.



Atas dasar ini, hanya konstruksi data dan pernyataan diimplementasikan, diikat bersama.


Mari kita cari tahu. Kami memiliki Konteks Penjadwalan - konteks umum penjadwalan startup. Ada fungsi data yang mengembalikan hasil perencanaan ini. Pada saat yang sama, kami membuat fungsi ekstensi dan pada saat yang sama pernyataan fungsi infiks, yang akan meluncurkan lambda yang memeriksa nilai-nilai kami.



Ada subjek, objek dan tindakan, dan Anda perlu menghubungkannya entah bagaimana. Dalam hal ini, hasil dari mengeksekusi data dengan kurung kurawal adalah subjek. Lambda yang kita berikan ke metode asersi adalah sebuah objek, dan metode asersi itu sendiri adalah sebuah aksi. Semua ini tampaknya tetap bersatu.



Berbicara tentang fungsi infix, penting untuk dipahami bahwa ini adalah langkah untuk menghilangkan sintaks yang berisik. Namun, kita harus memiliki subjek dan objek dari tindakan ini, dan kita perlu menggunakan pengubah infiks. Mungkin ada satu parameter - yaitu, parameter nol tidak bisa, dua tidak bisa, tiga - yah, Anda mengerti. Anda dapat meneruskan, misalnya, lambda ke fungsi ini, dan dengan cara ini konstruk yang belum Anda lihat sebelumnya diperoleh.

Mari kita beralih ke demo berikutnya. Lebih baik menonton video, dan tidak membaca teks.



Sekarang semuanya tampak siap: infix fungsi yang Anda lihat, ekstensi fungsi yang Anda lihat, deklarasi perusakan siap.

Mari kita kembali ke presentasi kita, dan di sini kita akan beralih ke satu poin yang agak penting ketika membangun bahasa yang berorientasi masalah - apa yang harus Anda pikirkan adalah kontrol konteks.



Ada beberapa situasi ketika kita dapat menggunakan DSL dan menggunakannya kembali di dalamnya, tetapi kami tidak ingin melakukan ini. Pengguna kami (mungkin pengguna yang tidak berpengalaman) menulis data di dalam data, dan itu tidak masuk akal. Kami ingin melarangnya melakukan hal ini.

Sebelum Kotlin versi 1.1, kami harus melakukan hal berikut: sebagai tanggapan terhadap fakta bahwa kami memiliki metode data dalam SchedulingContext , kami harus membuat metode data lain di DataContext , di mana kami menerima lambda (walaupun tanpa implementasi), kami harus menandai metode ini annotation @Deprecated dan beri tahu kompiler untuk tidak mengkompilasi ini. Anda melihat bahwa metode ini dimulai - jangan dikompilasi. Menggunakan pendekatan ini, kami bahkan mendapatkan beberapa pesan yang bermakna ketika kami menulis kode yang tidak berarti.



Setelah versi Kotlin 1.1, anotasi indah @DslMarker . Anotasi ini diperlukan untuk menandai anotasi yang diturunkan. Dengan mereka, pada gilirannya, kami akan menandai bahasa yang berorientasi masalah. Untuk setiap bahasa yang berorientasi masalah, Anda bisa membuat satu anotasi yang Anda tandai @DslMarker dan menggantungnya di setiap konteks yang diperlukan. Tidak perlu lagi menulis metode tambahan yang harus dilarang dikompilasi - semuanya berfungsi. Tidak dikompilasi.

Namun, ada satu kasus khusus ketika kami bekerja dengan model bisnis kami. Biasanya ditulis dalam Java. , , . , ? Student . – -, Kotlin .





- , : . , , .



.

  1. , . StudentContext. , . – , , , .
  2. – , , . . StudentContext , IStudent . , Student, IStudent StudentContext. DslMarker , .
  3. : deprecated . , . , . extension-, . .



, , , .



. . , , , . , . @DslMarker, . , @DslMarker, @Deprecated, , .

, :






-, DSL. , DSL . , , , .

, - , , , , - , . ? for β€” . DSL, , , DSL. this it. , Kotlin 1.2.20 , . , it.

. DSL, --, , . , . , , , - , , . . , - , ..

, . , - – DSL. , Kotlin-, . , DSL , , Kotlin- . -? Gradle-, , , , - . - , , – , DSL.



DSL' , . , . , DSL , , . - – . -, - . , - .
, Kotlin. , , , , , , . (, - , ), , DSL , , , . .

«», . , Kotlin . , , . , β€” , .
, DSL. , - . DSL, , 10 , , - . DSL – , , .

, . , Telegram: @ivan_osipov , Twitter: @_osipov_ , : i_osipov . .

Menit periklanan. JPoint β€” , 19-20 - Joker 2018 β€” Java-. . , .

Source: https://habr.com/ru/post/id416725/


All Articles