Jadi saya ingin mewarisi dari sealed class
dalam CSharp dan terbakar. Tidak ada cara untuk membuka segelnya kecuali Anda memiliki akses ke sumbernya.
Kemudian saya berpikir "mengapa sealed
ada?". 4 bulan lalu. Saya tidak bisa mengetahuinya, meskipun membaca banyak hal tentang itu, seperti:
- Harapan Jon Skeet " kelas disegel secara default di .NET. "
- Lebih suka komposisi daripada warisan?
- "Kamu seharusnya tidak menyegel semua kelas (...)"
- Bagaimana Anda mengejek kelas Tertutup?
Saya sudah mencoba mencerna semua itu sejak itu, tetapi itu terlalu banyak bagi saya. Akhirnya, kemarin saya mencobanya lagi. Saya telah memindai semuanya lagi, ditambah beberapa lainnya:
- Mengapa kelas harus selain "abstrak" atau "final / tertutup"?
- Dalam lebih dari 15 tahun pemrograman, pertama kali saya mendengar tentang SOLID , dari jawaban dari pertanyaan yang sudah ditautkan dan saya jelas tidak membacanya 4 bulan lalu
Akhirnya, setelah banyak merenungkan, saya memutuskan untuk berat mengedit pertanyaan asli berdasarkan judul baru.
Pertanyaan lama itu terlalu luas dan subyektif. Itu pada dasarnya bertanya:
- Dalam judul lama: Satu alasan bagus untuk menggunakan disegel
- Di dalam tubuh: Bagaimana cara memodifikasi kelas yang disegel dengan benar? Lupakan tentang warisan? Gunakan komposisi?
Tetapi sekarang, dengan memahami (yang saya tidak lakukan kemarin) bahwa semua yang sealed
dilakukan adalah mencegah warisan , dan kita dapat dan memang harus menggunakan komposisi daripada warisan , saya menyadari apa yang saya butuhkan adalah contoh - contoh praktis .
Saya kira pertanyaan saya di sini adalah (dan sebenarnya selalu) persis seperti yang disarankan Mr.Mindor dalam obrolan : Bagaimana mendesain pewarisan dapat menyebabkan biaya tambahan?
sumber
Jawaban:
Ini tidak begitu sulit untuk dipahami.
sealed
diciptakan secara khusus untuk Microsoft untuk membuat hidup mereka lebih mudah, menghemat banyak uang dan membantu reputasi mereka. Karena ini adalah fitur bahasa yang semua orang dapat menggunakannya juga tetapi ANDA mungkin tidak akan pernah perlu menggunakan yang disegel.Kebanyakan orang mengeluh tentang tidak dapat memperpanjang kelas dan jika mereka melakukannya maka mereka berkata baik semua orang tahu itu adalah tanggung jawab pengembang untuk membuatnya bekerja dengan benar. Itu benar, KECUALI orang-orang yang sama tidak memiliki petunjuk tentang sejarah Windows dan salah satu masalah
sealed
sedang mencoba untuk menyelesaikannya.Mari kita misalkan pengembang memperluas kelas inti .Net (karena disegel tidak ada) dan membuatnya bekerja dengan sempurna. Yay, untuk pengembang. Pengembang mengirimkan produk ke pelanggan. Aplikasi pengembang berfungsi dengan baik. Pelanggan itu senang. Hidup itu baik.
Sekarang Microsoft merilis sistem operasi baru, yang mencakup memperbaiki bug dalam kelas inti .Net khusus ini. Pelanggan ingin mengikuti perkembangan zaman dan memilih untuk menginstal sistem operasi baru. Tiba-tiba, aplikasi yang disukai pelanggan tidak lagi berfungsi, karena tidak memperhitungkan bug yang diperbaiki di kelas inti .Net.
Siapa yang disalahkan?
Mereka yang akrab dengan sejarah Microsoft tahu bahwa OS baru Microsoft akan disalahkan dan bukan perangkat lunak aplikasi yang menyalahgunakan pustaka windows. Jadi itu menjadi tugas Microsoft untuk memperbaiki masalah, bukan perusahaan aplikasi yang menciptakan masalah. Ini adalah salah satu alasan mengapa kode Windows menjadi kembung. Dari apa yang saya baca, kode sistem operasi Windows dikotori dengan spesifik if-then memeriksa apakah aplikasi dan versi spesifik sedang berjalan dan jika demikian maka lakukan beberapa pemrosesan khusus untuk memungkinkan program berfungsi. Itu banyak uang yang dihabiskan oleh Microsoft untuk memperbaiki ketidakmampuan perusahaan lain.
Menggunakan
sealed
tidak sepenuhnya menghilangkan skenario di atas, tetapi itu membantu.sumber
sealed
secara default, kecuali secara khusus tidak disegel, hanya karena begitu sedikit kelas yang sebenarnya dirancang untuk diwariskan. Ini jauh lebih mungkin bahwa kelas Anda tidak dibangun untuk mendukungnya, dan Anda harus memastikan bahwa Anda telah menghabiskan waktu dev itu jika Anda pergi keluar dari cara Anda untuk menandainya sebagai tidak tertutup.sealed
. Jika itu sebenarnya pilihan default maka itu berarti bahwa jika seseorang mewarisi dari suatu tipe maka mereka akan tahu bahwa itu dibangun untuk mendukungnya.String
dan kemudian mereka bermutasi setelah pemeriksaan keamanan tetapi sebelum I / O? Saya percaya bahwa jenis skenario adalah mengapa JavaString
ditandaifinal
(mirip dengansealed
) dan saya bertaruh logika yang sama mendukung beberapa keputusan C #.sealed
digunakan untuk menunjukkan bahwa penulis kelas belum mendesainnya untuk diwarisi. Tidak kurang, tidak lebih.Mendesain antarmuka dengan benar sudah cukup sulit bagi banyak programmer. Mendesain kelas dengan benar yang berpotensi diwariskan menambah kesulitan dan garis kode. Semakin banyak baris kode yang ditulis berarti semakin banyak kode yang harus dipelihara. Untuk menghindari kesulitan yang meningkat ini,
sealed
dapat menunjukkan bahwa kelas tidak pernah dimaksudkan untuk diwariskan, dan dalam konteks ini, menyegel kelas adalah solusi yang valid untuk masalah .Bayangkan kasus dari mana kelas
CustomBoeing787
tersebut berasalAirliner
. IniCustomBoeing787
menimpa beberapa metode yang dilindungi dariAirliner
, tetapi tidak semuanya. Jika pengembang memiliki cukup waktu dan tidak sia-sia, ia dapat menguji unit kelas untuk memastikan bahwa semuanya berfungsi seperti yang diharapkan, bahkan dalam konteks ketika metode non-overriden dipanggil (misalnya setter yang dilindungi yang mengubah keadaan objek). Jika pengembang yang sama (1) tidak punya waktu, (2) tidak ingin menghabiskan tambahan ratusan atau ribuan dolar dari bos / pelanggannya, atau (3) tidak ingin menulis kode tambahan dia tidak butuhkan sekarang (YAGNI), maka dia dapat:baik merilis kode di alam liar, mengingat bahwa itu adalah perhatian para programmer yang akan memelihara proyek ini nanti untuk peduli tentang bug potensial,
atau tandai kelas
sealed
untuk secara eksplisit menunjukkan kepada programmer masa depan bahwa kelas ini tidak dimaksudkan untuk diwariskan, dan bahwa membuka segelnya dan menggunakannya sebagai orangtua harus dilakukan dengan risiko Anda sendiri.Apakah ide yang bagus untuk menyegel kelas sebanyak mungkin, atau sesedikit mungkin? Dari pengalaman saya, tidak ada jawaban yang pasti. Beberapa pengembang cenderung menggunakan
sealed
terlalu banyak; yang lain tidak peduli tentang bagaimana kelas akan diwarisi dan masalah yang disebabkannya. Mengingat penggunaan tersebut, masalahnya mirip dengan yang terdiri dalam menentukan apakah programmer harus menggunakan dua atau empat ruang untuk lekukan: tidak ada jawaban yang benar.sumber
tl;dr
sini. Itu bisa berupa "Tidak, tidak ada satu pun alasan bagus" , atau "Saya pikir tidak ada alasan yang bagus, tetapi saya tidak tahu juga." . Bagi saya, "tidak ada jawaban pasti" adalah salah satunya.sealed
digunakan untuk menunjukkan bahwa penulis kelas belum mendesainnya untuk diwarisi. Itulah satu-satunya alasan bagus Anda.String
dan kemudian mereka bermutasi setelah pemeriksaan keamanan tetapi sebelum I / O? Saya percaya bahwa jenis skenario adalah mengapa JavaString
ditandaifinal
(mirip dengansealed
) dan saya bertaruh logika yang sama mendukung beberapa keputusan C #.Ketika sebuah kelas disegel, itu memungkinkan kode menggunakan tipe itu untuk tahu persis apa yang mereka hadapi.
Sekarang di C #
string
adalahsealed
, Anda tidak dapat mewarisi itu, tapi mari kita asumsikan sejenak bahwa itu tidak terjadi. Jika Anda melakukannya, maka seseorang dapat menulis kelas seperti ini:Ini sekarang akan menjadi kelas string yang melanggar semua jenis properti yang para desainer
string
tahu benar. Mereka tahu bahwaEquals
metodenya akan transitif, ini bukan. Heck, metode yang sama ini bahkan tidak simetris, karena string bisa sama dengan itu tetapi tidak akan sama dengan string itu. Saya yakin ada segala macam programmer yang memiliki kode tertulis dengan asumsi bahwaToString
metodestring
tidak akan membuang pengecualian untuk nilai-nilai non-nol. Anda sekarang dapat melanggar asumsi itu.Ada sejumlah kelas lain yang bisa kita gunakan untuk ini, dan segala macam asumsi lain yang bisa kita langgar. Jika Anda menghapus fitur bahasa untuk
sealed
maka perancang bahasa sekarang harus mulai menghitung hal-hal seperti ini di semua kode perpustakaan mereka. Mereka perlu melakukan segala macam pemeriksaan untuk memastikan bahwa tipe yang diperluas dari tipe yang ingin mereka gunakan akan ditulis "dengan benar", yang bisa menjadi hal yang sangat mahal untuk dilakukan (baik pada waktu dev, maupun pada saat run time). Dengan mampu menyegel kelas, mereka dapat memastikan bahwa ini tidak mungkin, dan bahwa setiap pernyataan yang mereka buat tentang detail implementasi dari tipe mereka sendiri tidak dapat dilanggar, kecuali untuk tipe-tipe yang sengaja dirancang untuk diperluas. Untuk jenis yang dirancang untuk diperpanjang Anda akan menemukan kode bahasa harus jauh lebih defensif tentang bagaimana menangani mereka.sumber
sealed
fitur built-in di sana yang tidak terpapar untuk kita gunakan. Itu masih tidak menjelaskan mengapa menggunakannya di luar lingkup yang sempit dan, karena sesempit kode kompiler , bahkan tidak menjelaskansealed
keberadaan.string
Tipe ini bukan kode kompiler. Itu bagian dari kode bahasa. Benar-benar berbeda. Bagaimanapun, saya hanya mengambil satu contoh. Anda bisa mengambil sebagian besar kelas tersegel di seluruh BCL dan menggunakannya sebagai contoh.Tingkatkan? Tidak sering. Saya hanya akan menggunakan disegel ketika saya memiliki tipe yang tidak dapat diubah (atau batasan lainnya) yang benar- benar harus tetap tidak dapat diubah dan saya tidak dapat mempercayai pewaris untuk memenuhi persyaratan tersebut tanpa memasukkan bug yang halus dan berbahaya ke dalam sistem.
Kami menggunakan warisan untuk hal-hal yang tidak standar. Bahkan jika komposisi lebih disukai daripada warisan (dan memang demikian), itu tidak berarti warisan harus dibuang. Ini berarti bahwa warisan memiliki beberapa masalah intrinsik yang diperkenalkan ke dalam desain dan pemeliharaan kode dan komposisi melakukan banyak hal yang sama dengan (secara umum) lebih sedikit masalah. Dan itu berarti bahwa bahkan jika komposisi disukai bahwa pewarisan itu baik untuk himpunan bagian masalah tertentu.
Saya tidak bermaksud terlalu kejam, tetapi jika Anda baru saja mendengar tentang SOLID dan pengujian unit, mungkin Anda harus keluar dari bawah batu Anda dan benar-benar menulis kode. Hal-hal yang tidak jelas dipotong, dan jarang akan "selalu melakukan X" atau "tidak pernah menggunakan Y" atau "Z adalah yang terbaik" menjadi cara yang benar (seperti tampaknya Anda tertarik berdasarkan pandangan pertanyaan Anda). Saat Anda menulis kode, Anda dapat melihat kasus-kasus di mana
sealed
bisa menjadi baik dan kasus-kasus di mana hal itu menyebabkan Anda bersedih - seperti pengorbanan lainnya.sumber
sealed
, tolong?Pertama-tama, Anda harus membaca posting Eric Lippert tentang mengapa Microsoft menyegel kelas mereka.
http://blogs.msdn.com/b/ericlippert/archive/2004/01/22/61803.aspx
Kedua, kata kunci yang disegel ada untuk melakukan apa yang dilakukannya - mencegah pewarisan. Ada banyak situasi di mana Anda ingin mencegah warisan. Pertimbangkan kelas yang Anda punya koleksi. Jika kode Anda bergantung pada kelas yang bekerja dengan cara tertentu, Anda tidak ingin seseorang menimpa salah satu metode atau properti di kelas itu dan menyebabkan aplikasi Anda gagal.
Ketiga, menggunakan disegel mendorong komposisi atas warisan, yang merupakan kebiasaan yang baik untuk dimasuki. Menggunakan komposisi lebih dari warisan berarti Anda tidak dapat melanggar prinsip substitusi Liskov dan Anda mengikuti prinsip Terbuka / Tertutup.
sealed
Oleh karena itu kata kunci yang membantu Anda dalam mengikuti dua dari lima prinsip terpenting pemrograman oo. Saya akan mengatakan itu menjadikannya fitur yang sangat berharga.sumber
Saya pikir pertanyaan ini tidak memiliki jawaban yang objektif dan semuanya tergantung pada perspektif Anda.
Di satu sisi, beberapa orang menginginkan segalanya "terbuka" dan beban untuk memastikan semuanya bekerja dengan benar adalah pada orang yang memperluas kelas. Inilah sebabnya mengapa kelas terbuka untuk warisan secara default. Dan mengapa metode Java semuanya virtual secara default.
Di sisi lain, beberapa ingin semuanya ditutup secara default dan memberikan opsi untuk membukanya. Dalam hal ini, pengarang kelas dasar yang perlu memastikan semua yang ia buat "terbuka" aman untuk digunakan dengan cara apa pun yang bisa dibayangkan. Inilah sebabnya mengapa dalam C # semua metode adalah non-virtual secara default dan perlu dinyatakan virtual untuk diganti. Ini juga untuk orang-orang itu, yang perlu menjadi cara untuk menghindari default "terbuka". Seperti membuat kelas tersegel / final, karena mereka melihat seseorang berasal dari kelas masalah besar untuk desain kelas mereka sendiri.
sumber
sealed
tidak bisa. Bagaimana kita bisa mewarisi dari mereka? Yang saya baca adalah kita tidak bisa. Pernah. Saya tidak tahu Saya sudah bolak-balik tentang hal ini selama 4 bulan sekarang, sejak saya pertama kali mendengar tentang disegel. Dan saya masih tidak tahu bagaimana melakukannya dengan benar ketika saya perlu memodifikasi apa pun pada kelas yang disegel. Jadi, saya tidak melihat "opsi untuk membukanya"!masalah dengan disegel dalam C # adalah bahwa itu agak final. Dalam 7 tahun pengembangan komersial C #, satu-satunya tempat saya telah melihat itu digunakan pada Microsoft .NET kelas di mana saya merasa saya punya alasan yang sah untuk memperluas kelas kerangka kerja untuk menerapkan perilaku yang disesuaikan, dan karena kelas disegel, saya tidak bisa .
Tidak mungkin untuk menulis kelas berpikir tentang setiap cara yang mungkin dapat digunakan, dan memutuskan bahwa tidak satu pun dari mereka yang sah. Demikian juga jika kerangka kerja keamanan tergantung pada kelas yang tidak diwariskan, maka Anda memiliki cacat desain keamanan.
Jadi, sebagai kesimpulan, saya dengan tegas berpendapat bahwa disegel tidak memiliki tujuan yang bermanfaat, dan tidak ada yang saya baca di sini sejauh ini telah mengubah pandangan itu.
Saya bisa melihat sejauh ini ada tiga argumen yang membenarkan disegel ditempatkan sejauh ini.
Argumen 1 adalah bahwa ia menghilangkan sumber bug ketika orang mewarisi dari kelas dan kemudian meningkatkan akses ke internal kelas itu tanpa benar-benar memahami internal dengan benar.
Argumen 2 adalah bahwa jika Anda memperpanjang kelas microsoft dan kemudian microsoft menerapkan perbaikan bug di kelas itu tetapi ekstensi Anda mengandalkan bug itu maka kode Anda sekarang rusak, microsoft disalahkan, dan disegel mencegah bahwa
Argumen 3 adalah bahwa sebagai pengembang kelas, pilihan saya adalah apakah Anda akan memperpanjangnya, sebagai bagian dari desain kelas saya.
Argumen 1 tampaknya merupakan argumen bahwa Anda para programmer akhir tidak tahu bagaimana menggunakan kode saya. Itu mungkin terjadi, tetapi jika Anda masih mengizinkan saya untuk mengakses objek antarmuka itu masih berlaku bahwa saya menggunakan objek Anda, dan membuat panggilan ke sana tanpa memahami cara menggunakannya, dan dengan demikian dapat membuat kerusakan sebanyak itu . Ini adalah justifikasi yang lemah akan perlunya disegel.
Saya mengerti dari mana basis faktual untuk arg 2 berasal. Khususnya ketika microsoft melakukan pemeriksaan tambahan OS pada win32 api call untuk mengidentifikasi panggilan yang salah, yang telah meningkatkan stabilitas umum Windows XP dibandingkan dengan Windows NT, tetapi dengan biaya jangka pendek banyak aplikasi yang rusak ketika Windows XP dirilis. Microsoft menyelesaikan ini dengan memasukkan 'aturan' agar cek ini dapat dikatakan, abaikan ketika aplikasi X melanggar aturan ini, ini merupakan bug yang dikenal
Argumen 2 adalah argumen yang buruk karena disegel paling tidak membuat perbedaan dengan ini karena jika ada bug di perpustakaan .NET Anda tidak punya pilihan selain mencari pekerjaan, dan ketika perbaikan microsoft keluar, kemungkinan besar berarti pekerjaan Anda di sekitar yang tergantung pada perilaku yang rusak sekarang rusak. Apakah Anda menyiasatinya dengan menggunakan warisan atau kode pembungkus tambahan lainnya, saat kode itu diperbaiki, pekerjaan Anda di sekitar akan rusak.
Argumen 3 adalah satu-satunya argumen yang memiliki substansi apa pun untuk membenarkan dirinya sendiri. Namun masih lemah. Itu hanya bertentangan dengan butir penggunaan kembali kode.
sumber
It is impossible to write a class thinking about every possible way it could be used
- Itulah alasan mengapa banyak kelas Framework disegel ... Ini menghilangkan kebutuhan untuk memikirkan setiap cara yang mungkin dilakukan seseorang untuk memperluas kelas.Saya hanya menggunakan
sealed
kata kunci di kelas yang hanya berisi metode statis.sumber
static
gantinya?Seperti yang dibuktikan oleh pertanyaan saya, saya bukan ahli di sini. Tetapi saya tidak melihat alasan untuk
sealed
bahkan ada.Tampaknya dibuat untuk membantu arsitek kode untuk mendesain antarmuka. Jika Anda ingin menulis kelas yang akan keluar di alam liar dan banyak orang akan mewarisi darinya, memodifikasi apa pun di kelas itu dapat merusaknya karena terlalu banyak orang. Jadi kita bisa menyegelnya dan tidak ada yang bisa mewarisinya. "Masalah terpecahkan".
Yah, sepertinya "menutupi tempat dan mengungkap yang lain". Dan itu bahkan tidak menyelesaikan masalah - itu hanya menghilangkan alat : warisan. Sebagai alat apa pun, ia memiliki banyak penggunaan dan mewarisi dari kelas yang tidak stabil adalah risiko yang Anda ambil. Siapa pun yang mengambil risiko itu harus tahu lebih baik.
Mungkin ada kata kunci
stable
, jadi orang akan tahu lebih aman untuk mewarisi kata kunci tersebut.sumber
sealed
itu bukan alat yang berguna bagi perancang kerangka kerja. Kelas dalam suatu kerangka kerja harus kotak hitam; Anda tidak perlu tahu tentang internal kelas untuk dapat menggunakannya dengan benar. Namun itulah yang dituntut oleh warisan.