Ekstensi dalam Dart (Flutter)

Dalam rilis terbaru dari Dart 2.6, bahasa memperkenalkan fungsi baru, ekstensi statis atau metode ekstensi statis, yang memungkinkan Anda untuk menambahkan metode baru ke jenis yang ada. Mengapa kita perlu ekstensi? Bagaimana cara menggunakannya dan apa gunanya?



Pendahuluan


Untuk mulai dengan, apa ekstensi secara umum? Ekstensi adalah gula sintaksis yang memperluas kelas yang ada di tempat yang berbeda dari modul deklarasi kelas.

Dalam pemrograman, metode ekstensi sudah ada sejak lama, sehingga mereka harus melesat. Ekstensi secara aktif digunakan dalam bahasa seperti C #, Java via Manifold, Swift, Kotlin dan banyak lainnya.

Masalah


Katakanlah kita memiliki metode catchError, yang hanya mengerikan dan perlu ditulis ulang ke fungsi keren baru. Misalkan dia menggunakan fungsi tipe apa pun sebagai argumen, alih-alih fungsi yang diketik ketat atau tipe cek, dan ini karena 8 bulan yang lalu ketika mengembangkan fungsi ini, itu logis pada waktu itu.

Hal pertama yang terlintas dalam pikiran adalah untuk menulis ulang fungsi ini, tetapi di sini kita dihadapkan dengan masalah yang terjadi terlalu sering dalam proyek, dan mengubah fungsi akan menyebabkan ketidakmampuan operasi seluruh proyek.

Nah, jika opsi pertama bukan untuk kita. cocok, karena alasan logis, maka saya dapat menerapkan fungsi Masa Depan baru yang akan memenuhi semua persyaratan saya.

abstract class Future<T> { ... /// Catches any [error] of type [E]. Future<T> onError<E>(FutureOr<T> handleError(E error, StackTrace stack)) => this.catchError(...   -  ...); } ... } 

dan saya akan memanggilnya seperti ini:

 Future<String> someString = ...; someString.onError((FormatException e, s) => ...).then(...); 

Sayangnya, saya tidak bisa menambahkan fungsi ini ke kelas Future. Jika saya melakukan ini, saya juga akan menambahkannya ke antarmuka Future, dan setiap kelas lain yang mengimplementasikan antarmuka ini tidak lengkap dan tidak lagi dikompilasi.

Nah, opsi lain adalah mengimplementasikan fungsi pihak ketiga yang akan terlihat seperti ini:

 Future<T> onFutureError<T, E>(Future<T> source, FutureOr<T> handleError(E error, StackTrace stack)) => source.catchError(... - ...); 

Dan panggilannya akan terlihat seperti ini:

 Future<String> someString = ...; onFutureError(someString, (FormatException e, s) => ...).then(...); 

Hebat, semuanya bekerja! Tetapi menyedihkan bahwa itu mulai sangat dibaca. Kami menggunakan metode. yang diimplementasikan di dalam kelas, sehingga mereka dipanggil -.doingSomething (); Kode ini dapat dimengerti, saya hanya membacanya dari kiri ke kanan dan berdiri di kepala saya serangkaian peristiwa. Menggunakan fungsi pembantu membuat kode rumit dan kurang dapat dibaca.

Kalau begitu, saya bisa menerapkan kelas baru dan membiarkan pengguna membungkus antarmuka lama mereka dengan fungsionalitas yang ditingkatkan.

 class CustomFuture<T> { CustomFuture(Future<T> future) : _wrapper = future; Future<T> _wrapper; Future<T> onError<E>(FutureOr<T> handleError(E error, StackTrace stack)) => _wrapper.catchError(...-     ...); } 

dan panggilan akan terlihat seperti ini:

 Future<String> someString = ...; CustomFuture(someString).onError((FormatException e, s) => ...).then(...); 

Tampak hebat!

Memecahkan masalah dengan ekstensi


Segera setelah kami menghentikan pemrograman dalam pascal dan kembali ke 2019, implementasi fungsi ini akan dikurangi menjadi ukuran ini:

 extension CustomFuture <T> on Future<T> { Future<T> onError<E>( FutureOr<T> handleError(E error, StackTrace stack)) => this.catchError(...something clever...); } 

dan seperti inilah panggilannya:

 Future<String> someString = ...; someString.onError((FormatException e, s) => ...).then(...); 

Itu saja! Solusi untuk masalah ini hanya membutuhkan 5 baris kode. Kamu Anda mungkin bertanya-tanya jenis sihir apa dan bagaimana cara kerjanya.

Bahkan, ia berperilaku dengan cara yang sama seperti kelas pembungkus, meskipun dalam kenyataannya itu hanya fungsi statis tambahan. Ekstensi memungkinkan Anda melepaskan tulisan pembungkus eksplisit.

Ini bukan pembungkus


Desain ekstensi bekerja sedemikian rupa sehingga terlihat seperti deklarasi kelas yang ada, tetapi bertindak seolah-olah itu adalah pembungkus dengan _wrapper pribadi. Tetapi ada satu keuntungan dibandingkan dengan kelas pembungkus, ini mengakses kelas itu sendiri secara langsung, daripada mengakses kelas pembungkus _wrapper.

Fitur ini tidak dibuat demi fitur, tetapi seperti yang saya katakan sebelumnya, ekstensi memang cara yang lebih mudah untuk memanggil fungsi statis. Ini berarti tidak ada objek pembungkus.

Semuanya statis


Saya mengatakan "metode ekstensi statis" di atas, dan saya melakukannya karena suatu alasan!

Dart diketik secara statis. Kompiler mengetahui tipe setiap ekspresi pada waktu kompilasi, jadi jika Anda menulis user.age (19) dan usia adalah ekstensi, maka kompiler harus mencari tahu tipe apa yang dibungkus dalam objek yang diberikan untuk menemukan tipe seluruh panggilan.

Masalah apa yang bisa muncul?


Contoh paling sederhana dari masalah dengan ekstensi adalah ketika Anda memiliki lebih dari satu ekstensi dalam cakupannya. Pada dasarnya, pemenang adalah ekstensi yang paling dekat dengan jenis ekspresi aktual yang Anda panggil anggota, dengan beberapa pemesanan.

Cara termudah untuk menyelesaikan masalah adalah menyambungkan secara ketat ekstensi yang Anda butuhkan, atau Anda dapat menggunakan ekstensi secara eksplisit:

 ... List list = ...; MyList(list).printlist(); SomeList(list).printlist(); ... extension MyList on List { void printlist() { print(...- ...); } } extension SomeList on List { void printlist() { print(...-  ...); } } 

Ringkasan


  • Bahasa panah memiliki alat yang nyaman untuk memperluas fungsionalitas yang ada.
  • Anda dapat memperluas metode, operator, setter dan getter, tetapi tidak bidang.
  • Anda dapat menggunakan metode ekstensi secara eksplisit atau - ketika tidak ada konflik dengan anggota antarmuka atau ekstensi lainnya secara implisit.
  • Panggilan implisit berfungsi seperti panggilan eksplisit.
  • Ekstensi bersifat statis. Segala sesuatu tentang mereka diselesaikan berdasarkan tipe statis.

Jika output ekstensi gagal karena ekstensi yang bertentangan, maka Anda dapat melakukan salah satu dari yang berikut:

  1. Terapkan ekstensi secara eksplisit.
  2. Impor ekstensi yang bertentangan dengan awalan, karena itu tidak tersedia untuk panggilan implisit.
  3. Jangan mengimpor ekstensi yang bertentangan sama sekali.

Itu saja! Anda dapat menggunakan ekstensi secara maksimal.

Dan tentu saja, tautan yang bermanfaat:

Situs web bergetar
Situs web dart
Di mana saya dapat membaca lebih lanjut tentang ekstensi
Saluran telegram tempat saya berbicara tentang semua yang terbaru di dunia Flutter dan tidak hanya

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


All Articles