Menghilangkan warisan dalam bahasa pemrograman

10

Saya mengembangkan bahasa pemrograman saya sendiri. Ini adalah bahasa tujuan umum (pikir Python mengetik statis untuk desktop, yaitu int x = 1;) tidak dimaksudkan untuk cloud.

Apakah Anda pikir tidak apa-apa untuk tidak mengizinkan warisan atau Mixin? (mengingat bahwa pengguna setidaknya memiliki antarmuka)

Misalnya: Google Go, Bahasa sistem yang mengejutkan komunitas pemrograman dengan tidak mengizinkan warisan.

Christopher
sumber

Jawaban:

4

Bahkan, kami menemukan semakin banyak yang menggunakan pewarisan kurang optimal karena (antara lain) itu mengarah ke kopling ketat.

Warisan implementasi selalu dapat digantikan oleh pewarisan antarmuka plus komposisi, dan desain perangkat lunak yang lebih modern cenderung mengarah pada penggunaan pewarisan lebih sedikit dan lebih sedikit, mendukung komposisi.

Jadi ya , tidak memberikan warisan khususnya adalah keputusan desain yang sepenuhnya valid, dan sangat populer .

Mixin, di sisi lain, bahkan belum (belum) fitur bahasa utama, dan bahasa yang memang menyediakan fitur yang disebut "mixin" sering memahami hal-hal yang sangat berbeda dengannya. Merasa bebas untuk menyediakannya, atau tidak. Secara pribadi saya merasa sangat berguna tetapi mengimplementasikannya dengan benar mungkin sangat sulit.

Konrad Rudolph
sumber
13

Penalaran tentang apakah pewarisan (atau fitur tunggal, benar-benar) diperlukan atau tidak tanpa juga mempertimbangkan sisa bahasa semantik tidak ada gunanya; Anda berdebat dalam ruang hampa.

Yang Anda butuhkan adalah filosofi desain bahasa yang konsisten; bahasa harus mampu menyelesaikan masalah secara elegan. Model untuk mencapai ini mungkin atau mungkin tidak memerlukan warisan, tetapi sulit untuk menilai ini tanpa gambaran besar.

Jika, misalnya, bahasa Anda memiliki fungsi kelas satu, aplikasi fungsi parsial, tipe data polimorfik, variabel tipe dan tipe generik, Anda telah cukup banyak membahas basis yang sama seperti yang Anda miliki dengan warisan OOP klasik, tetapi menggunakan paradigma yang berbeda.

Jika Anda memiliki pengikatan yang terlambat, pengetikan dinamis, metode-sebagai-properti, argumen fungsi fleksibel, dan fungsi kelas satu, Anda juga membahas alasan yang sama, tetapi sekali lagi, menggunakan paradigma yang berbeda.

(Menemukan contoh untuk dua paradigma yang diuraikan dibiarkan sebagai latihan untuk pembaca.)

Jadi, pikirkan tentang jenis semantik yang Anda inginkan, bermain-main dengan mereka dan lihat apakah mereka cukup tanpa warisan. Jika tidak, Anda dapat memutuskan untuk memasukkan warisan ke dalam campuran, atau Anda dapat memutuskan bahwa ada sesuatu yang hilang.

tammmer
sumber
9

Iya.

Saya pikir tidak mengizinkan warisan baik-baik saja, terutama jika bahasa Anda diketik secara dinamis. Anda dapat mencapai penggunaan kembali kode yang serupa dengan, misalnya, delegasi atau komposisi. Sebagai contoh, saya telah menulis beberapa program cukup rumit - baik saja, tidak yang rumit:) - dalam JavaScript tanpa warisan apapun. Saya pada dasarnya menggunakan objek sebagai struktur data aljabar dengan beberapa fungsi yang dilampirkan sebagai metode. Saya juga memiliki banyak fungsi yang bukan metode yang beroperasi pada objek-objek ini.

Jika Anda memiliki pengetikan dinamis - dan saya berasumsi Anda melakukannya - Anda dapat memiliki polimorfisme tanpa pewarisan juga. Juga, jika Anda mengizinkan menambahkan metode arbitrer ke objek saat runtime, Anda tidak akan terlalu membutuhkan hal-hal seperti mixin.

Pilihan lain - yang saya pikir bagus - adalah meniru JavaScript dan menggunakan prototipe. Ini lebih sederhana daripada kelas namun juga sangat fleksibel. Hanya sesuatu yang perlu dipertimbangkan.

Jadi, semua bilang, aku akan melakukannya.

Tikhon Jelvis
sumber
2
Selama ada cara yang masuk akal untuk menyelesaikan jenis tugas yang biasanya ditangani dengan warisan, silakan. Anda dapat mencoba beberapa kasus penggunaan, untuk memastikan (bahkan mungkin sampai sejauh meminta contoh masalah dari orang lain, untuk menghindari bias diri ...)
comingstorm
Tidak Ini statis dan sangat diketik, saya akan menyebutkan itu di atas.
Christopher
Karena diketik secara statis, Anda tidak bisa hanya menambahkan metode acak ke objek saat runtime. Namun, Anda masih bisa melakukan itik mengetik: lihat protokol Gosu untuk contoh. Saya menggunakan protokol sedikit selama musim panas dan mereka sangat berguna. Gosu juga memiliki "perangkat tambahan" yang merupakan cara menambahkan metode ke kelas setelah fakta. Anda dapat mempertimbangkan untuk menambahkan sesuatu seperti itu juga.
Tikhon Jelvis
2
Tolong, tolong, tolong, berhenti mengaitkan pewarisan dan penggunaan kembali kode. Sudah lama diketahui bahwa pewarisan adalah alat yang sangat buruk untuk penggunaan kembali kode.
Jan Hudec
3
Warisan hanya satu alat untuk menggunakan kembali kode. Seringkali itu adalah alat yang sangat bagus , dan di lain waktu itu mengerikan. Memilih komposisi daripada warisan bukan berarti tidak pernah menggunakan warisan sama sekali.
Michael K
8

Ya, ini adalah keputusan desain yang masuk akal untuk menghilangkan warisan.

Sebenarnya ada alasan yang sangat baik untuk menghapus warisan implementasi, karena dapat menghasilkan beberapa kode yang sangat kompleks dan sulit untuk dipelihara. Saya bahkan lebih jauh menganggap warisan (seperti yang biasanya diterapkan di sebagian besar bahasa OOP) sebagai kesalahan.

Clojure, misalnya, tidak menyediakan warisan implementasi, lebih memilih untuk menyediakan serangkaian fitur ortogonal (protokol, data, fungsi, makro) yang dapat digunakan untuk mencapai hasil yang sama tetapi jauh lebih bersih.

Berikut adalah video yang saya temukan sangat mencerahkan tentang topik umum ini, di mana Rich Hickey mengidentifikasi sumber mendasar kompleksitas dalam bahasa pemrograman (termasuk warisan) dan menyajikan alternatif untuk masing-masing: Sederhana menjadi mudah

mikera
sumber
Dalam banyak kasus, pewarisan antarmuka lebih tepat. Tetapi jika digunakan dengan moderasi, warisan implentasi dapat memungkinkan penghapusan banyak kode boilerplate, dan merupakan solusi paling elegan. Itu hanya membutuhkan programmer yang lebih cerdas dan lebih berhati-hati daripada monyet Python / JavaScript rata-rata.
Erik Alapää
4

Ketika saya pertama kali menemukan fakta bahwa kelas VB6 tidak mendukung warisan (hanya antarmuka), itu benar-benar membuat saya kesal (dan masih).

Namun, alasannya sangat buruk adalah karena tidak memiliki parameter konstruktor juga, jadi Anda tidak bisa melakukan injeksi ketergantungan normal (DI). Jika Anda memiliki DI maka itu lebih penting daripada warisan karena Anda dapat mengikuti prinsip komposisi yang disukai daripada warisan. Itu cara yang lebih baik untuk menggunakan kembali kode.

Tidak memiliki Mixin? Jika Anda ingin mengimplementasikan antarmuka dan mendelegasikan semua pekerjaan antarmuka itu ke objek yang ditetapkan melalui injeksi dependensi, maka Mixin sangat ideal. Kalau tidak, Anda harus menulis semua kode boilerplate untuk mendelegasikan setiap metode dan / atau properti ke objek anak. Saya melakukannya banyak (terima kasih kepada C #) dan itu satu hal yang saya harap tidak perlu saya lakukan.

Scott Whitlock
sumber
Ya apa yang mendorong pertanyaan ini adalah implementasi DOM SVG di mana penggunaan kembali kode sangat bermanfaat.
Christopher
2

Untuk tidak setuju dengan jawaban lain: tidak, Anda membuang fitur ketika mereka tidak kompatibel dengan sesuatu yang Anda inginkan lebih. Java (dan bahasa-bahasa GC'd lainnya) membuang manajemen memori eksplisit karena ia ingin mengetik lebih aman. Haskell membuang mutasi karena ingin lebih banyak alasan rasional dan suka jenis. Bahkan C membuang (atau menyatakan ilegal) jenis aliasing dan perilaku lainnya karena ia ingin lebih mengoptimalkan kompiler.

Jadi pertanyaannya adalah: Apa yang Anda inginkan lebih dari sekadar warisan?

Ryan Culpepper
sumber
1
Hanya manajemen memori eksplisit dan keamanan tipe yang sama sekali tidak terkait dan jelas tidak kompatibel. Hal yang sama berlaku untuk mutasi dan "tipe mewah". Saya setuju dengan alasan umum tetapi contoh Anda agak buruk.
Konrad Rudolph
@KonradRudolph, manajemen memori dan keamanan tipe sangat terkait. A free/ deleteoperasi memberi Anda kemampuan untuk membatalkan referensi; kecuali sistem tipe Anda dapat melacak semua referensi yang terpengaruh, itu membuat bahasa tidak aman. Secara khusus, C atau C ++ tidak aman. Memang benar bahwa Anda dapat membuat kompromi dalam satu atau yang lain (misalnya tipe linier atau batasan alokasi) untuk membuat mereka setuju. Lebih tepatnya saya seharusnya mengatakan bahwa Java menginginkan keamanan jenis dengan sistem tipe khusus yang sederhana dan alokasi tidak terbatas lebih banyak.
Ryan Culpepper
Yah itu lebih tergantung pada bagaimana Anda mendefinisikan keamanan jenis. Sebagai contoh, jika Anda mengambil definisi (sepenuhnya masuk akal) dari keamanan tipe kompilasi-waktu, dan ingin integritas referensi menjadi bagian dari sistem tipe Anda, maka Java juga bukan tipe aman (karena memungkinkan nullreferensi). Larangan freeadalah pembatasan tambahan yang cukup sewenang-wenang. Singkatnya, apakah suatu bahasa adalah tipe aman tergantung pada definisi Anda tentang keamanan jenis daripada pada bahasa.
Konrad Rudolph
1
@Ryan: Pointer adalah tipe yang aman. Mereka selalu berperilaku sesuai dengan definisi mereka. Mungkin tidak seperti yang Anda inginkan, tetapi selalu menurut cara mereka didefinisikan. Anda mencoba meregangkan mereka menjadi sesuatu yang bukan milik mereka. Pointer pintar dapat menjamin keamanan memori secara sepele dalam C ++.
DeadMG
@KonradRudolph: Di Jawa, setiap Treferensi merujuk pada salah satu nullatau objek kelas yang diperluas T; nulljelek, tetapi operasi nullmelempar pengecualian yang jelas daripada merusak tipe invarian di atas. Kontras dengan C ++: setelah panggilan ke delete, T*pointer mungkin menunjuk ke memori yang tidak lagi memegang Tobjek. Lebih buruk lagi, melakukan penugasan bidang menggunakan penunjuk itu dalam penugasan, Anda mungkin memperbarui bidang objek yang sama sekali berbeda dari kelas jika kebetulan ditempatkan di alamat terdekat. Itu bukan tipe keselamatan oleh definisi istilah yang berguna.
Ryan Culpepper
1

Tidak.

Jika Anda ingin menghapus fitur bahasa dasar, maka Anda secara universal menyatakan bahwa itu tidak pernah diperlukan (atau sulit untuk diimplementasikan secara tidak adil, yang tidak berlaku di sini). Dan "tidak pernah" adalah kata yang kuat dalam rekayasa perangkat lunak. Anda akan membutuhkan pembenaran yang sangat kuat untuk membuat pernyataan seperti itu.

DeadMG
sumber
8
Itu hampir tidak benar: seluruh desain Java adalah tentang menghapus fitur seperti operator-overloading, multiple inheritance dan manajemen memori manual. Selain itu, saya pikir memperlakukan fitur bahasa apa pun sebagai "suci" adalah salah; alih-alih membenarkan menghapus suatu fitur, Anda harus membenarkan untuk menambahkannya - Saya tidak dapat memikirkan fitur apa pun yang harus dimiliki setiap bahasa.
Tikhon Jelvis
6
@TikhonJelvis: Itulah sebabnya Jawa adalah bahasa yang mengerikan , dan tidak ada artinya kecuali "GUNAKAN Warisan yang DIKUMPULKAN GARBAGE " adalah alasan nomor satu mengapa. Fitur bahasa harus dibenarkan, saya setuju, tapi yang ini adalah fitur bahasa dasar - ia memiliki banyak aplikasi yang berguna dan tidak dapat direplikasi oleh programmer tanpa melanggar KERING.
DeadMG
1
@DeadMG wow! Bahasa yang cukup kuat.
Christopher
@DeadMG: Saya mulai menulis sanggahan sebagai komentar tetapi kemudian saya mengubahnya menjadi jawaban (qv).
Ryan Culpepper
1
"Kesempurnaan dicapai bukan ketika tidak ada yang tersisa untuk ditambahkan tetapi ketika tidak ada yang tersisa untuk dihapus" (q)
9000
1

Berbicara dari perspektif C ++ yang ketat, pewarisan berguna untuk dua tujuan utama:

  1. Ini memungkinkan resusabilitas kode
  2. Dalam kombinasi dengan menimpa di kelas anak, ini memungkinkan Anda untuk menggunakan pointer kelas dasar untuk menangani ke objek kelas anak tanpa mengetahui jenisnya.

Untuk poin 1 selama Anda memiliki beberapa cara untuk berbagi kode tanpa harus terlalu banyak berakrobat, Anda dapat menghapus warisan. Untuk poin 2. Anda bisa menggunakan cara java dan bersikeras antarmuka untuk mengimplementasikan fitur ini.

Manfaat menghapus warisan adalah

  1. mencegah hierarki panjang dan masalah terkait. Mekanisme pembagian kode Anda harus cukup baik untuk itu.
  2. Hindari perubahan dalam kelas induk dari melanggar kelas anak.

Pengorbanan sebagian besar antara "Dont Repeat Yourself" dan Fleksibilitas di satu sisi dan penghindaran masalah di sisi lain. Secara pribadi saya akan benci untuk melihat warisan dari C ++ hanya karena beberapa pengembang lain mungkin tidak cukup pintar untuk melihat masalah.

DPD
sumber
1

Apakah Anda pikir tidak apa-apa untuk tidak mengizinkan warisan atau Mixin? (mengingat bahwa pengguna setidaknya memiliki antarmuka)

Anda dapat melakukan banyak pekerjaan yang sangat berguna tanpa implementasi warisan atau mixin, tapi saya ingin tahu apakah Anda harus memiliki beberapa jenis antarmuka warisan, yaitu, deklarasi yang mengatakan jika suatu objek mengimplementasikan antarmuka A maka ia juga perlu antarmuka B (yaitu, A adalah spesialisasi B dan ada jenis hubungan). Di sisi lain, objek Anda yang dihasilkan hanya perlu merekam bahwa ia mengimplementasikan kedua antarmuka, sehingga tidak ada terlalu banyak kerumitan di sana. Semua bisa dilakukan dengan sempurna.

Kurangnya warisan implementasi memiliki satu kelemahan jelas: Anda tidak akan dapat membangun vtables yang diindeks numerik untuk kelas Anda dan karenanya harus melakukan pencarian hash untuk setiap pemanggilan metode (atau mencari cara cerdas untuk menghindarinya). Ini bisa menyakitkan jika Anda merutekan nilai fundamental (misalnya, angka) melalui mekanisme ini. Bahkan implementasi hash yang sangat baik bisa mahal ketika Anda menekannya berkali-kali di setiap loop batin!

Donal Fellows
sumber