Mengapa menutup kelas?

96

Saya ingin mendengar apa motivasi di balik sebagian besar kelas tertutup dalam kerangka kerja .Net. Apa manfaat menyegel kelas? Saya tidak dapat memahami bagaimana tidak mengizinkan warisan dapat berguna dan kemungkinan besar bukan satu-satunya yang melawan kelas-kelas ini.

Jadi, mengapa kerangka kerja dirancang seperti ini dan bukankah itu akan menjadi perubahan besar untuk membuka segel semuanya? Pasti ada alasan lain selain menjadi jahat?

mmiika
sumber

Jawaban:

41
  • Terkadang kelas terlalu berharga dan tidak dirancang untuk diwariskan.
  • Runtime / Reflection dapat membuat asumsi pewarisan tentang kelas tersegel saat mencari tipe. Contoh yang bagus untuk ini adalah - Atribut direkomendasikan untuk ditutup untuk kecepatan waktu proses pencarian. type.GetCustomAttributes (typeof (MyAttribute)) akan bekerja lebih cepat secara signifikan jika MyAttribute disegel.

Artikel MSDN untuk topik ini membatasi Ekstensibilitas oleh Kelas Sealing .

CVertex
sumber
3
Senang melihat mereka dengan jelas mengatakan "gunakan dengan hati-hati" sekarang ... berharap mereka akan mempraktikkan apa yang mereka khotbahkan.
mmiika
13
Sepertinya nasihat yang buruk bagi saya :(
Jon Skeet
4
@CVertex: Maaf, saya tidak mencoba mengkritik Anda - hanya artikelnya.
Jon Skeet
16
@Generalt: Saya percaya dalam mendesain untuk warisan atau melarangnya. Mendesain untuk pewarisan membutuhkan kerja keras dan sering kali akan membatasi implementasi di masa mendatang. Warisan juga menimbulkan ketidakpastian kepada penelepon tentang tujuan panggilan mereka. Itu juga tidak bercampur dengan baik dengan kekekalan (yang saya penggemar). Saya hanya menemukan warisan kelas berguna di sejumlah tempat yang relatif kecil (sedangkan saya suka antarmuka).
Jon Skeet
1
@CVertex Jika Anda telah menggunakan .NET kemungkinan besar Anda mengalami masalah dan tidak menyadarinya, hampir semua kelas inti .NET disegel.
CoryG
105

Kelas harus dirancang untuk warisan atau melarangnya. Ada biaya untuk mendesain warisan:

  • Itu dapat menentukan implementasi Anda (Anda harus mendeklarasikan metode mana yang akan memanggil metode lain mana, jika pengguna mengganti satu tetapi tidak yang lain)
  • Ini mengungkapkan implementasi Anda, bukan hanya efeknya
  • Artinya, Anda harus memikirkan lebih banyak kemungkinan saat mendesain
  • Hal-hal seperti Persamaan sulit untuk dirancang di pohon warisan
  • Ini membutuhkan lebih banyak dokumentasi
  • Jenis yang tidak dapat diubah yang disubkelas dapat menjadi dapat berubah (ick)

Item 17 dari Java Efektif menjelaskan lebih detail tentang ini - terlepas dari fakta bahwa itu ditulis dalam konteks Java, saran ini berlaku untuk .NET juga.

Secara pribadi saya berharap kelas disegel secara default di .NET.

Jon Skeet
sumber
26
Hmm .. jika Anda memperpanjang kelas, bukan masalah Anda jika Anda melanggarnya?
mmiika
25
Bagaimana jika perubahan implementasi Anda tidak memiliki kendali atas di kelas dasar merusak Anda? Salah siapa itu? Pada dasarnya, warisan memperkenalkan kerapuhan. Lebih mengutamakan komposisi daripada warisan mempromosikan ketahanan, IMO.
Jon Skeet
6
Ya, antarmuka bagus - dan ya, Anda tetap dapat menyukai komposisi. Tetapi jika saya mengekspos kelas dasar yang tidak disegel tanpa memikirkannya dengan sangat hati-hati, saya harus berharap bahwa perubahan mungkin akan merusak kelas turunan. Itu terasa seperti hal yang buruk bagiku. Lebih baik menutup kelas dan menghindari kerusakan, IMO.
Jon Skeet
8
@ Joan: Komposisi adalah hubungan "memiliki-a" daripada "is-a". Jadi jika Anda ingin menulis kelas yang dapat bertindak seperti daftar dalam beberapa cara, tetapi tidak dengan cara lain, Anda mungkin ingin membuat kelas dengan variabel anggota Daftar <T>, daripada berasal dari Daftar <T>. Anda kemudian akan menggunakan daftar tersebut untuk menerapkan berbagai metode.
Jon Skeet
3
@ThunderGr: Tapi itulah maksud saya: ketika Anda tidak perlu khawatir tentang implementasi lain yang disediakan oleh subclass, Anda bisa lebih bebas dengan implementasi Anda sendiri. Misalnya, salah satu metode diimplementasikan dengan memanggil metode lain, dan keduanya virtual. Itu perlu didokumentasikan - meskipun itu terasa seperti detail implementasi. Pada dasarnya, mendesain untuk warisan menambahkan borgol - yang sesuai dalam beberapa kasus, tetapi tidak dalam kasus lain. Saya lebih suka memiliki kelas tertutup yang mengimplementasikan antarmuka daripada kelas yang tidak disegel dengan banyak metode virtual.
Jon Skeet
8

Tampaknya pedoman resmi Microsoft tentang penyegelan telah berevolusi sejak pertanyaan ini diajukan ~ 9 tahun yang lalu, dan mereka beralih dari filosofi keikutsertaan (segel secara default) ke menyisih (jangan segel secara default):

X JANGAN menutup kelas tanpa alasan yang kuat untuk melakukannya.

Menyegel kelas karena Anda tidak dapat memikirkan skenario ekstensibilitas bukanlah alasan yang baik. Pengguna framework suka mewarisi dari kelas karena berbagai alasan yang tidak jelas, seperti menambahkan anggota kenyamanan. Lihat Unsealed Classes untuk contoh alasan yang tidak diketahui pengguna ingin mewarisi dari suatu tipe.

Alasan yang baik untuk memeteraikan kelas mencakup yang berikut:

  • Kelas tersebut adalah kelas statis. Lihat Desain Kelas Statis.
  • Kelas menyimpan rahasia sensitif keamanan dalam anggota yang dilindungi yang diwariskan.
  • Kelas mewarisi banyak anggota virtual dan biaya penyegelan mereka secara individual akan lebih besar daripada manfaat meninggalkan kelas tidak disegel.
  • Kelas adalah atribut yang membutuhkan pencarian waktu proses yang sangat cepat. Atribut yang disegel memiliki tingkat kinerja yang sedikit lebih tinggi daripada yang tidak disegel. Lihat Atribut.

X JANGAN menyatakan dilindungi atau anggota virtual pada tipe tertutup.

Menurut definisi, tipe tersegel tidak dapat diwariskan. Ini berarti bahwa anggota yang dilindungi pada tipe tersegel tidak dapat dipanggil, dan metode virtual pada tipe tersegel tidak dapat diganti.

✓ PERTIMBANGKAN penyegelan anggota yang Anda timpa. Masalah yang dapat diakibatkan dari memperkenalkan anggota virtual (dibahas dalam Anggota Virtual) juga berlaku untuk penggantian, meskipun pada tingkat yang sedikit lebih rendah. Sealing sebuah override melindungi Anda dari masalah ini mulai dari titik tersebut dalam hierarki pewarisan.

Memang, jika Anda mencari basis kode ASP.Net Core , Anda hanya akan menemukan sekitar 30 kejadian sealed class, yang sebagian besar merupakan atribut dan kelas uji.

Menurut saya, konservasi keabadian adalah argumen yang baik untuk mendukung penyegelan.

Ohad Schneider
sumber
4

Saya menemukan kalimat ini dalam dokumentasi msdn: "Kelas tertutup terutama digunakan untuk mencegah penurunan. Karena mereka tidak pernah dapat digunakan sebagai kelas dasar, beberapa pengoptimalan waktu berjalan dapat membuat panggilan anggota kelas tertutup sedikit lebih cepat."

Saya tidak tahu apakah pertunjukan adalah satu-satunya keuntungan dari kelas tersegel dan secara pribadi saya juga ingin mengetahui alasan lain ...

bruno conde
sumber
4
Akan menarik untuk melihat manfaat kinerja seperti apa yang mereka bicarakan ...
mmiika
3

Kinerja merupakan faktor penting misalnya, kelas string di java bersifat final (<- disegel) dan alasannya adalah kinerja saja. Saya pikir poin penting lainnya adalah untuk menghindari masalah kelas dasar rapuh yang dijelaskan secara rinci di sini: http://blogs.msdn.com/ericlippert/archive/2004/01/07/virtual-methods-and-brittle-base-classes. aspx

Jika Anda menyediakan kerangka kerja, penting untuk proyek lama pemeliharaan dan untuk meningkatkan kerangka kerja Anda untuk menghindari masalah kelas dasar yang rapuh

Peter Parker
sumber
Alasan mengapa String di java menjadi final bukanlah kinerja, itu keamanan.
CesarB
@ CesarB: Ya, tetapi juga, String bukan kelas Java normal. Ini adalah satu-satunya kelas (saya percaya) di Java yang mendukung overloading operator (untuk lebih lanjut, lihat di sini , bagian: "Bahkan C dan Java memiliki (hardcode) operator overloading"), yang tidak mungkin dilakukan di kelas normal. Karena itu, Stringkelas tersebut bahkan mungkin tidak mungkin untuk membuat subkelas, meskipun itu belum final.
wchargin
1

Sealed digunakan untuk mencegah "masalah kelas dasar rapuh". Saya menemukan artikel bagus di MSDN yang menjelaskan hal itu.

ihebiheb
sumber
0

Sealing memungkinkan Anda menyadari beberapa peningkatan kinerja kecil. Ini kurang benar di dunia JIT dan pesimisasi malas daripada di dunia, katakanlah C ++, tetapi karena .NET tidak sebagus pesimisasi seperti kompiler java sebagian besar karena filosofi desain yang berbeda, itu masih berguna. Ini memberi tahu kompiler bahwa ia bisa langsung memanggil metode virtual apa pun daripada memanggilnya secara tidak langsung melalui vtable.

Ini juga penting ketika Anda menginginkan 'dunia tertutup' untuk hal-hal seperti perbandingan kesetaraan. Biasanya begitu saya mendefinisikan metode virtual, saya cukup tersirat untuk mendefinisikan gagasan perbandingan kesetaraan yang benar-benar mengimplementasikan gagasan itu. Di sisi lain, saya mungkin dapat mendefinisikannya untuk subkelas tertentu dari kelas dengan metode virtual. Menyegel kelas itu memastikan bahwa kesetaraan benar-benar berlaku.

Edward KMETT
sumber
0

Menyegel kelas membuat pengelolaan sumber daya sekali pakai lebih mudah.

Jeff Dunlop
sumber
0

Untuk menentukan apakah akan menyegel kelas, metode, atau properti, Anda biasanya harus mempertimbangkan dua poin berikut:

• Manfaat potensial yang mungkin diperoleh dari kelas-kelas melalui kemampuan untuk menyesuaikan kelas Anda.

• Potensi yang menurunkan kelas dapat mengubah kelas Anda sedemikian rupa sehingga kelas tidak lagi berfungsi dengan benar atau seperti yang diharapkan.

pengguna3629577
sumber
0

Pertimbangan lebih lanjut adalah bahwa kelas tertutup tidak dapat dihentikan dalam pengujian unit Anda. Dari dokumentasi Microsoft :

Class yang disegel atau metode statis tidak dapat di-stub karena jenis stub bergantung pada pengiriman metode virtual. Untuk kasus seperti itu, gunakan jenis shim seperti yang dijelaskan dalam Menggunakan shims untuk mengisolasi aplikasi Anda dari rakitan lain untuk pengujian unit

Dave Clark
sumber