Neden bir sınıfı mühürleyelim?


96

.Net çerçevesindeki mühürlü sınıfların çoğunun arkasındaki motivasyonun ne olduğunu duymak isterim. Bir sınıfı mühürlemenin faydası nedir? Mirasa izin vermemenin nasıl yararlı olabileceğini ve büyük olasılıkla bu sınıflarla savaşan tek kişinin olmadığını anlayamıyorum.

Öyleyse, çerçeve neden bu şekilde tasarlandı ve her şeyi açığa çıkarmak için sarsılmaz bir değişiklik olmaz mı? Kötü olmaktan başka bir sebep daha olmalı?



Yanıtlar:


41
  • Bazen sınıflar çok değerlidir ve miras alınacak şekilde tasarlanmamıştır.
  • Runtime / Reflection, tür ararken mühürlenmiş sınıflar hakkında miras varsayımları yapabilir. Bunun harika bir örneği şudur: Arama çalışma zamanı hızı için özniteliklerin mühürlenmesi önerilir. type.GetCustomAttributes (typeof (MyAttribute)), MyAttribute mühürlenirse önemli ölçüde daha hızlı performans gösterir.

Bu konuyla ilgili MSDN makalesi, Genişletilebilirliği Sızdırmazlık Sınıflarına Göre Sınırlandırma konusudur .


3
Şimdi açıkça "dikkatli kullanın" dediklerini görmek güzel ... keşke vaaz ettiklerini uygulasalar.
mmiika

13
Bu bana kötü bir tavsiye gibi görünüyor :(
Jon Skeet

4
@CVertex: Üzgünüm, sizi eleştirmeye çalışmıyordum - sadece makale.
Jon Skeet

16
@generalt: Miras için tasarlamaya veya onu yasaklamaya inanıyorum. Miras için tasarım yapmak oldukça fazla çalışma gerektirir ve gelecekte uygulamaları genellikle sınırlandırır. Kalıtım ayrıca arayanlara tam olarak neyi arayacakları konusunda belirsizlik de getirir. Ayrıca, değişmezlikle (hayranıyım) iyi karışmaz. Sınıf mirasını yalnızca nispeten az sayıda yerde yararlı buluyorum (oysa arayüzleri seviyorum).
Jon Skeet

1
@CVertex .NET kullandıysanız, büyük olasılıkla sorunla karşılaşırsınız ve fark etmediniz, neredeyse tüm .NET çekirdek sınıfları mühürlenmiştir.
CoryG

105

Sınıflar ya kalıtım için tasarlanmalı ya da bunu yasaklamalıdır. Miras için tasarım yapmanın bir maliyeti vardır:

  • Uygulamanızı sabitleyebilir (bir kullanıcı birini geçersiz kılar ancak diğerini geçersiz kılarsa, hangi yöntemlerin diğer yöntemleri çağıracağını belirtmeniz gerekir)
  • Yalnızca efektlerden ziyade uygulamanızı ortaya çıkarır
  • Tasarım yaparken daha fazla olasılık düşünmeniz gerektiği anlamına gelir
  • Eşittir gibi şeyleri bir miras ağacında tasarlamak zordur
  • Daha fazla belge gerektirir
  • Alt sınıflara ayrılmış bir değişmez tür değişebilir (ick)

Etkili Java'nın 17. Maddesi bu konuda daha fazla ayrıntıya giriyor - Java bağlamında yazılmış olmasına bakılmaksızın, tavsiye .NET için de geçerlidir.

Şahsen ben sınıfların varsayılan olarak .NET'te mühürlenmesini diliyorum.


26
Hmm .. bir dersi uzatırsan, bozarsan senin sorunun olmaz mı?
mmiika

25
Ya temel sınıfta üzerinde hiçbir kontrolünüz olmayan bir uygulama değişikliği sizi kırarsa? Bu kimin hatası? Kalıtım, temelde kırılganlık getirir. Miras yerine kompozisyonu tercih etmek, sağlamlığı teşvik eder, IMO.
Jon Skeet

6
Evet, arayüzler güzel - ve evet, yine de kompozisyonu tercih edebilirsiniz. Ancak, mühürlenmemiş bir temel sınıfı çok dikkatli düşünmeden ifşa edersem, değişikliklerin türetilmiş sınıfları pekala bozabileceğini beklemeliyim. Bu bana kötü bir şey gibi geliyor. Sınıfı mühürlemek ve kırılmayı önlemek daha iyidir, IMO.
Jon Skeet

8
@Joan: Beste, "is-a" olmaktan çok "bir-bir" ilişkisidir. Öyleyse, bazı şekillerde liste gibi davranan ancak diğerlerinde olmayan bir sınıf yazmak istiyorsanız, List <T> 'den türetmek yerine List <T> üye değişkenine sahip bir sınıf oluşturmak isteyebilirsiniz. Daha sonra listeyi çeşitli yöntemleri uygulamak için kullanırsınız .
Jon Skeet

3
@ThunderGr: Ama benim açımdan şu: Alt sınıflar tarafından sağlanan diğer uygulamaların neler olduğu konusunda endişelenmenize gerek olmadığında, kendi uygulamanızla daha özgür olabilirsiniz. Örneğin, bir yöntemin başka bir yöntemi çağırarak uygulandığını ve her ikisinin de sanal olduğunu varsayalım. O halde - Bu ihtiyaçlar belgelenmiş olması hissediyor bir uygulama ayrıntı gibi. Temel olarak, miras için tasarım yapmak kelepçeler ekler - bunlar bazı durumlarda uygun, ancak bazılarında uygun değildir. Bir grup sanal yöntemle mühürlenmemiş bir sınıf yerine, bir arabirim uygulayan mühürlenmiş bir sınıfa sahip olmayı tercih ederim.
Jon Skeet

8

Görünüşe göre mühürleme ile ilgili resmi Microsoft yönergeleri, bu soru yaklaşık 9 yıl önce sorulduğundan beri gelişti ve bir tercih felsefesinden (varsayılan olarak mühür) vazgeçmeye (varsayılan olarak mühürleme) geçtiler:

X Bunu yapmak için iyi bir neden olmadan sınıfları KAPATMAYIN .

Bir genişletilebilirlik senaryosu düşünemediğiniz için bir sınıfı kapatmak iyi bir neden değildir. Çerçeve kullanıcıları, kolaylık üyeleri eklemek gibi çeşitli açık olmayan nedenlerle sınıflardan miras almayı severler. Kullanıcıların bir türden miras almak istemesinin açık olmayan nedenlerinin örnekleri için Mühürsüz Sınıflar'a bakın.

Bir sınıfı mühürlemek için iyi nedenler şunları içerir:

  • Sınıf, statik bir sınıftır. Statik Sınıf Tasarımı konusuna bakın.
  • Sınıf, güvenliğe duyarlı sırları miras alınan korumalı üyelerde depolar.
  • Sınıf, birçok sanal üyeyi miras alır ve onları ayrı ayrı mühürlemenin maliyeti, sınıfı mühürsüz bırakmanın faydalarından daha ağır basar.
  • Sınıf, çok hızlı çalışma zamanı araması gerektiren bir özniteliktir. Mühürlü nitelikler, mühürlenmemiş olanlardan biraz daha yüksek performans seviyelerine sahiptir. Özniteliklere bakın.

X Mühürlü türlerde korumalı veya sanal üyeler BİLDİRMEYİN.

Tanım olarak, mühürlenmiş türler kaynağından miras alınamaz. Bu, mühürlü türlerdeki korumalı üyelerin çağrılamayacağı ve mühürlenmiş türlerdeki sanal yöntemlerin geçersiz kılınamayacağı anlamına gelir.

Geçersiz kıldığınız sızdırmazlık elemanlarını DİKKAT EDİN. Sanal üyelerin tanıtılmasından kaynaklanabilecek sorunlar (Sanal Üyeler'de tartışılmıştır), biraz daha az olsa da, geçersiz kılmalar için de geçerlidir. Bir geçersiz kılmayı mühürlemek, miras hiyerarşisindeki o noktadan başlayarak sizi bu sorunlardan korur.

Aslında, ASP.Net Core kod tabanını ararsanız sealed class, çoğu öznitelikler ve test sınıfları olmak üzere yalnızca yaklaşık 30 oluşum bulacaksınız .

Değişmezliğin korunmasının mühürleme lehine iyi bir argüman olduğunu düşünüyorum.


4

Bu cümleyi msdn belgelerinde buldum: "Mühürlü sınıflar öncelikle türetmeyi önlemek için kullanılır. Asla bir temel sınıf olarak kullanılamayacakları için, bazı çalışma zamanı optimizasyonları mühürlü sınıf üyelerini biraz daha hızlı aramayı sağlayabilir."

Mühürlü derslerin tek avantajı performans mı bilmiyorum ve kişisel olarak başka nedenleri de bilmek istiyorum ...


4
Ne tür bir performans avantajından bahsettiklerini görmek ilginç olurdu ...
mmiika

3

Performans önemli bir faktördür, örneğin, java'daki string sınıfı nihaidir (<- sealed) ve bunun nedeni yalnızca performanstır. Bence bir başka önemli nokta, burada ayrıntılı olarak açıklanan kırılgan temel sınıf probleminden kaçınmaktır: http://blogs.msdn.com/ericlippert/archive/2004/01/07/virtual-methods-and-brittle-base-classes. aspx

Bir çerçeve sağlarsanız, sürdürülebilirlik eski projeleri için ve kırılgan temel sınıf sorununu önlemek için çerçevenizi yükseltmek önemlidir.


Java'da String'in nihai olmasının nedeni performans değil güvenliktir.
CesarB

@CesarB: Evet, ama aynı zamanda, String normal bir Java sınıfı değil. O (bkz fazlası için destekler operatör aşırı olduğunu Java sadece (sanırım) sınıftır burada normal bir sınıfta mümkün olmadığı, hangi: "Hatta C ve Java (eklenmiş) operatörü aşırı yükleme var" bölümü). Bu nedenle, son Stringolmasa bile , sınıfın alt sınıflara ayrılması bile mümkün olmayabilir.
wchargin

1

Mühürlü, "kırılgan taban sınıfı problemini" önlemek için kullanılır. MSDN'de bunu açıklayan iyi bir makale buldum .


0

Sızdırmazlık, bazı küçük performans kazanımları elde etmenizi sağlar. Bu, JIT dünyasında ve tembel kötümserlik dünyasında, örneğin C ++ dünyasında daha az doğrudur, ancak .NET, java derleyicilerinin çoğunlukla farklı tasarım felsefeleri nedeniyle kötümserleştirme kadar iyi olmadığı için, yine de yararlıdır. Derleyiciye, herhangi bir sanal yöntemi vtable aracılığıyla dolaylı olarak çağırmak yerine doğrudan çağırabileceğini söyler.

Eşitlik karşılaştırması gibi şeyler için 'kapalı bir dünya' istediğinizde de önemlidir. Normalde bir kez sanal bir yöntem tanımladığımda, fikri gerçekten uygulayan bir eşitlik karşılaştırması kavramını tanımlamam gerekiyor. Öte yandan, sanal yöntemle sınıfın belirli bir alt sınıfı için tanımlayabilirim. Bu sınıfı mühürlemek, eşitliğin gerçekten geçerli olmasını sağlar.


0

Bir sınıfı mühürlemek, tek kullanımlık kaynakları yönetmeyi kolaylaştırır.


0

Bir sınıfı, yöntemi veya özelliği mühürleyip mühürlemeyeceğinizi belirlemek için genellikle aşağıdaki iki noktayı göz önünde bulundurmalısınız:

• Sınıfınızı özelleştirme becerisi sayesinde sınıf türetmenin kazanabileceği potansiyel faydalar.

• Sınıf türetme potansiyeli, sınıflarınızı artık doğru veya beklendiği gibi çalışmayacak şekilde değiştirebilir.


0

Dikkat edilmesi gereken başka bir nokta da, mühürlenmiş sınıfların birim testlerinize eklenemeyeceğidir. Gönderen Microsoft'un dokümanlarına :

Mühürlenmiş sınıflar veya statik yöntemler, saplama türleri sanal yöntem dağıtımına dayandığından, stubed edilemez. Bu tür durumlarda, birim testi için uygulamanızı diğer montajlardan izole etmek için şim kullanma bölümünde açıklandığı gibi şim türlerini kullanın.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.