Bisakah kita mengekspos antarmuka di Ruby seperti yang kita lakukan di java dan menerapkan modul atau kelas Ruby untuk mengimplementasikan metode yang ditentukan oleh antarmuka.
Salah satu caranya adalah dengan menggunakan inheritance dan method_missing untuk mencapai hal yang sama, tetapi adakah pendekatan lain yang lebih sesuai yang tersedia?
Jawaban:
Ruby memiliki Antarmuka seperti bahasa lainnya.
Perhatikan bahwa Anda harus berhati-hati untuk tidak menyamakan konsep Antarmuka , yang merupakan spesifikasi abstrak dari tanggung jawab, jaminan, dan protokol dari sebuah unit dengan konsep
interface
yang merupakan kata kunci dalam pemrograman Java, C # dan VB.NET bahasa. Di Ruby, kami menggunakan yang pertama sepanjang waktu, tetapi yang terakhir tidak ada.Sangat penting untuk membedakan keduanya. Yang penting adalah Antarmuka , bukan
interface
. Iniinterface
memberi tahu Anda tidak ada yang berguna. Tidak ada yang mendemonstrasikan ini lebih baik daripada antarmuka marker di Java, yang merupakan antarmuka yang tidak memiliki anggota sama sekali: lihat sajajava.io.Serializable
danjava.lang.Cloneable
; keduanya memilikiinterface
arti yang sangat berbeda, namun mereka memiliki tanda tangan yang persis sama .Jadi, jika dua
interface
itu memiliki arti yang berbeda, memiliki tanda tangan yang sama, apa sebenarnya yanginterface
menjamin Anda?Contoh bagus lainnya:
Apa Antarmuka dari
java.util.List<E>.add
?element
ada di dalam koleksiDan yang mana yang benar-benar muncul di
interface
? Tidak ada! Tidak ada apa pun di dalamnyainterface
yang mengatakan bahwaAdd
metode tersebut bahkan harus menambahkan sama sekali, itu mungkin juga menghapus elemen dari koleksi.Ini adalah implementasi yang sangat valid dari itu
interface
:Contoh lain: di mana
java.util.Set<E>
sebenarnya dikatakan bahwa itu, Anda tahu, satu set ? Tidak kemana-mana! Atau lebih tepatnya, dalam dokumentasi. Dalam Bahasa Inggris.Di hampir semua kasus
interfaces
, baik dari Java dan .NET, semua informasi yang relevan sebenarnya ada di dokumen, bukan di jenisnya. Jadi, jika tipe tidak memberi tahu Anda sesuatu yang menarik, mengapa tetap menyimpannya? Mengapa tidak hanya berpegang pada dokumentasi? Dan itulah yang dilakukan Ruby.Perhatikan bahwa ada bahasa lain di mana Antarmuka sebenarnya dapat dijelaskan dengan cara yang berarti. Namun, bahasa-bahasa itu biasanya tidak memanggil konstruksi yang mendeskripsikan Antarmuka "
interface
", mereka menyebutnyatype
. Dalam bahasa pemrograman yang diketik secara dependen, Anda dapat, misalnya, mengekspresikan properti yangsort
dikembalikan oleh suatu fungsi dengan panjang yang sama dengan aslinya, bahwa setiap elemen yang ada dalam aslinya juga ada dalam koleksi yang diurutkan dan tidak ada elemen yang lebih besar. muncul sebelum elemen yang lebih kecil.Jadi, singkatnya: Ruby tidak memiliki padanan dengan Java
interface
. Ini tidak , bagaimanapun, memiliki setara dengan Java Antarmuka , dan itu persis sama dengan di Jawa: dokumentasi.Juga, seperti di Java, Tes Penerimaan juga dapat digunakan untuk menentukan Antarmuka .
Khususnya, di Ruby, Antarmuka suatu objek ditentukan oleh apa yang dapat dilakukannya , bukan apa
class
yang ada, atau apamodule
yang digabungkannya. Objek apa pun yang memiliki<<
metode dapat ditambahkan. Ini sangat berguna dalam pengujian unit, di mana Anda dapat dengan mudah meneruskanArray
atauString
bukan yang lebih rumitLogger
, meskipunArray
danLogger
tidak membagikan eksplisitinterface
selain fakta bahwa keduanya memiliki metode yang dipanggil<<
.Contoh lain adalah
StringIO
, yang mengimplementasikan sama Antarmuka sebagaiIO
dan dengan demikian sebagian besar dari Antarmuka dariFile
, tetapi tanpa berbagi apapun nenek moyang selainObject
.sumber
interface
tidak berguna, kehilangan tujuan penggunaannya. Akan lebih mudah untuk mengatakan bahwa ruby diketik secara dinamis dan memiliki fokus yang berbeda dalam pikiran dan membuat konsep seperti IOC tidak perlu / tidak diinginkan. Ini adalah perubahan yang sulit jika Anda terbiasa Desain dengan Kontrak. Something Rails bisa mendapatkan keuntungan, yang disadari oleh tim inti seperti yang Anda lihat di versi terbaru.interface
mungkin tidak memberikan semua info yang relevan, tetapi menyediakan tempat yang jelas untuk meletakkan dokumentasi. Saya telah menulis kelas di Ruby yang mengimplementasikan (cukup) IO, tetapi saya melakukannya dengan trial and error dan tidak terlalu senang dengan prosesnya. Saya juga telah menulis beberapa implementasi antarmuka saya sendiri, tetapi mendokumentasikan metode mana yang diperlukan dan apa yang seharusnya mereka lakukan sehingga anggota lain dari tim saya dapat membuat implementasi terbukti merupakan tantangan.interface
membangun memang hanya diperlukan untuk mengobati berbagai jenis sebagai yang sama dalam bahasa tunggal warisan statis diketik (misalnya mengobatiLinkedHashSet
danArrayList
baik sebagaiCollection
), ia memiliki cukup banyak tidak ada yang dengan Antarmuka sebagai jawaban ini menunjukkan. Ruby tidak diketik secara statis sehingga tidak diperlukan konstruksi .Coba "contoh bersama" rspec:
https://www.relishapp.com/rspec/rspec-core/v/3-5/docs/example-groups/shared-examples
Anda menulis spesifikasi untuk antarmuka Anda dan kemudian meletakkan satu baris di setiap spesifikasi pelaksana, mis.
Contoh lengkapnya:
Pembaruan : Delapan tahun kemudian (2020) ruby sekarang memiliki dukungan untuk antarmuka yang diketik secara statis melalui sorbet. Lihat Kelas dan Antarmuka Abstrak di dokumentasi sorbet.
sumber
Ruby tidak memiliki fungsionalitas itu. Pada prinsipnya, ini tidak membutuhkannya karena Ruby menggunakan apa yang disebut mengetik bebek .
Ada beberapa pendekatan yang bisa Anda lakukan.
Tulis implementasi yang memunculkan pengecualian; jika subclass mencoba menggunakan metode yang tidak diimplementasikan, itu akan gagal
Bersamaan dengan di atas, Anda harus menulis kode pengujian yang memberlakukan kontrak Anda (apa yang posting lain di sini salah sebut Antarmuka )
Jika Anda menemukan diri Anda menulis metode void seperti di atas setiap saat, maka tulis modul pembantu yang menangkapnya
Sekarang, gabungkan yang di atas dengan modul Ruby dan Anda mendekati apa yang Anda inginkan ...
Dan kemudian Anda bisa melakukannya
Izinkan saya menekankan sekali lagi: ini belum sempurna, karena segala sesuatu di Ruby terjadi saat runtime; tidak ada pemeriksaan waktu kompilasi. Jika Anda memasangkan ini dengan pengujian, maka Anda seharusnya dapat menemukan kesalahan. Lebih jauh lagi, jika Anda mengambil langkah di atas lebih jauh, Anda mungkin bisa menulis Antarmuka yang melakukan pemeriksaan pada kelas pertama kali objek dari kelas itu dibuat; membuat tes Anda sesederhana menelepon
MyCollection.new
... ya, di atas :)sumber
Seperti yang dikatakan semua orang di sini, tidak ada sistem antarmuka untuk ruby. Namun melalui introspeksi, Anda bisa menerapkannya sendiri dengan cukup mudah. Berikut adalah contoh sederhana yang dapat ditingkatkan dalam banyak cara untuk membantu Anda memulai:
Menghapus salah satu metode yang dideklarasikan pada Person atau mengubahnya, jumlah argumen akan memunculkan a
NotImplementedError
.sumber
Tidak ada yang namanya antarmuka dalam cara Java. Tapi ada hal lain yang bisa Anda nikmati di ruby.
Jika Anda ingin menerapkan beberapa jenis dan antarmuka - sehingga objek dapat diperiksa apakah mereka memiliki beberapa metode / pesan yang Anda butuhkan darinya -, Anda kemudian dapat melihat rubycontracts . Ini mendefinisikan mekanisme yang mirip dengan PyProtocols . Sebuah blog tentang pengecekan tipe ruby ada di sini .
Pendekatan yang disebutkan bukanlah proyek hidup, meskipun tujuannya tampaknya bagus pada awalnya, tampaknya sebagian besar pengembang ruby dapat hidup tanpa pemeriksaan tipe yang ketat. Tetapi fleksibilitas ruby memungkinkan untuk mengimplementasikan pemeriksaan tipe.
Jika Anda ingin memperluas objek atau kelas (hal yang sama di ruby) dengan perilaku tertentu atau agak memiliki cara ruby untuk beberapa pewarisan, gunakan
include
atauextend
mekanisme. Denganinclude
Anda dapat memasukkan metode dari kelas atau modul lain ke dalam suatu objek. Denganextend
Anda dapat menambahkan perilaku ke kelas, sehingga instance-nya akan memiliki metode tambahan. Itu penjelasan yang sangat singkat.Saya berpendapat cara terbaik untuk menyelesaikan kebutuhan antarmuka Java adalah dengan memahami model objek ruby (lihat kuliah Dave Thomas misalnya). Mungkin Anda akan melupakan antarmuka Java. Atau Anda memiliki aplikasi luar biasa di jadwal Anda.
sumber
Seperti yang ditunjukkan oleh banyak jawaban, tidak ada cara di Ruby untuk memaksa kelas mengimplementasikan metode tertentu, dengan mewarisi dari kelas, termasuk modul atau yang serupa. Alasannya mungkin karena prevalensi TDD di komunitas Ruby, yang merupakan cara berbeda untuk mendefinisikan antarmuka - pengujian tidak hanya menentukan tanda tangan metode, tetapi juga perilakunya. Jadi, jika Anda ingin mengimplementasikan kelas yang berbeda, yang mengimplementasikan beberapa antarmuka yang sudah ditentukan, Anda harus memastikan bahwa semua tes lulus.
Biasanya tes didefinisikan secara terpisah menggunakan tiruan dan rintisan. Tetapi ada juga alat seperti Bogus , yang memungkinkan untuk menentukan uji kontrak. Tes semacam itu tidak hanya mendefinisikan perilaku kelas "primer", tetapi juga memeriksa bahwa metode yang dipotong ada di kelas yang bekerja sama.
Jika Anda benar-benar peduli dengan antarmuka di Ruby, saya akan merekomendasikan menggunakan kerangka kerja pengujian yang mengimplementasikan pengujian kontrak.
sumber
Semua contoh di sini menarik tetapi kehilangan validasi kontrak Antarmuka, maksud saya jika Anda ingin objek Anda menerapkan semua definisi metode Antarmuka dan hanya yang ini Anda tidak bisa. Jadi saya usulkan Anda contoh sederhana yang cepat (dapat ditingkatkan pasti) untuk memastikan Anda memiliki apa yang Anda harapkan melalui Antarmuka Anda (Kontrak).
pertimbangkan Antarmuka Anda dengan metode yang ditentukan seperti itu
Kemudian Anda dapat menulis objek dengan setidaknya kontrak Antarmuka:
Anda dapat memanggil Objek Anda dengan aman melalui Antarmuka untuk memastikan Anda persis seperti yang didefinisikan Antarmuka
Dan Anda juga dapat memastikan Objek Anda mengimplementasikan semua definisi metode Antarmuka Anda
sumber
Saya telah menyampaikan sedikit jawaban carlosayam untuk kebutuhan tambahan saya. Ini menambahkan beberapa penegakan dan opsi tambahan ke kelas Antarmuka:
required_variable
danoptional_variable
yang mendukung nilai default.Saya tidak yakin Anda ingin menggunakan pemrograman meta ini dengan sesuatu yang terlalu besar.
Seperti jawaban lain yang telah dinyatakan, Anda sebaiknya menulis tes yang dengan benar menegakkan apa yang Anda cari, terutama setelah Anda ingin mulai menerapkan parameter dan mengembalikan nilai.
Peringatan bahwa metode ini hanya memberikan kesalahan pada pemanggilan kode. Pengujian akan tetap diperlukan untuk penegakan yang tepat sebelum waktu proses.
Contoh Kode
interface.rb
plugin.rb
Saya menggunakan perpustakaan tunggal untuk pola tertentu yang saya gunakan. Dengan cara ini setiap subclass mewarisi pustaka tunggal saat mengimplementasikan "antarmuka" ini.
my_plugin.rb
Untuk kebutuhan saya, ini mengharuskan kelas yang mengimplementasikan subkelas "antarmuka" itu.
sumber
Ruby sendiri tidak memiliki padanan yang tepat untuk antarmuka di Java.
Namun, karena antarmuka semacam itu terkadang sangat berguna, saya mengembangkan permata untuk Ruby sendiri, yang mengemulasi antarmuka Java dengan cara yang sangat sederhana.
Ini namanya
class_interface
.Ini bekerja cukup sederhana. Pertama instal permata dengan
gem install class_interface
atau tambahkan ke Gemfile dan rund Andabundle install
.Mendefinisikan antarmuka:
Menerapkan antarmuka itu:
Jika Anda tidak menerapkan konstanta atau metode tertentu atau nomor parameter tidak cocok, kesalahan terkait akan dimunculkan sebelum program Ruby dijalankan. Anda bahkan dapat menentukan tipe konstanta dengan menetapkan tipe di antarmuka. Jika nihil, semua jenis diperbolehkan.
Metode "mengimplementasikan" harus dipanggil pada baris terakhir kelas, karena itu adalah posisi kode di mana metode yang diimplementasikan di atas sudah diperiksa.
Selengkapnya di: https://github.com/magynhard/class_interface
sumber
Saya menyadari bahwa saya menggunakan pola "Kesalahan tidak diterapkan" terlalu banyak untuk pemeriksaan keamanan pada objek yang saya inginkan untuk perilaku tertentu. Akhirnya menulis permata yang pada dasarnya memungkinkan untuk menggunakan antarmuka seperti ini:
Itu tidak memeriksa argumen metode. Itu seperti versi0.2.0
. Contoh lebih rinci di https://github.com/bluegod/rintsumber