Ana akım güçlü statik OOP dilleri neden ilkel miras almayı önlüyor?


53

Bu neden tamam ve çoğunlukla bekleniyor:

abstract type Shape
{
   abstract number Area();
}

concrete type Triangle : Shape
{
   concrete number Area()
   {
      //...
   }
}

... bu tamam değilken ve hiç kimse şikayet etmez:

concrete type Name : string
{
}

concrete type Index : int
{
}

concrete type Quantity : int
{
}

Motivasyonum derleme zamanı doğruluğu doğrulaması için tip sisteminin kullanımını en üst düzeye çıkarmak.

Not: evet, okudum bu ve sarma iş çevresinde bir hacky olduğunu.


1
Yorumlar uzun tartışmalar için değildir; bu konuşma sohbete taşındı .
maple_shaft

Bu soruda da benzer bir motivasyon yaşadım, ilginç bulabilirsiniz.
default.kramer

Ben de "miras istemiyoruz" fikrini onaylayan bir cevap eklemek için gittiğini ve bu sarma olduğunu olacak size düşündüren özellikle JIT optimizasyonlar ile, örtülü veya açık döküm (veya başarısızlıklar) istediğiniz hangisi sana verdiği dahil çok güçlü Yine de neredeyse aynı performansı elde edersiniz, ancak bu cevaba bağlandınız :-) Sadece ekleyeceğim, eğer diller özellikleri / yöntemleri iletmek için gerekli olan kaynak kodunu düşürmek için özellikler eklerse, özellikle de sadece tek bir değer varsa, iyi olurdu.
Mark Hurd

Yanıtlar:


82

Java ve C # gibi dilleri düşündüğünüzü düşünüyorum.

Bu dillerde, ilkel (benzeri int) temel olarak performans için bir uzlaşmadır. Nesnelerin tüm özelliklerini desteklemiyorlar, ancak daha hızlı ve daha az ek yüke sahipler.

Nesnelerin mirası desteklemesi için, her bir vakanın hangi sınıfın bir örneği olduğunu çalışma zamanında "bilmesi" gerekir. Aksi halde, geçersiz kılınan yöntemler çalışma zamanında çözülemez. Bu, nesneler için örnek verinin, sınıf nesnesine bir işaretçi ile birlikte bellekte depolandığı anlamına gelir. Eğer bu bilgiler aynı zamanda ilkel değerlerle birlikte saklanacaksa, hafıza gereksinimleri şişirilecektir. 16 bitlik bir tamsayı değeri, değer için 16 biti ve ayrıca bir işaretçi için sınıfına 32 veya 64 bit belleği gerektirir.

Bellek yükünün yanı sıra, aritmetik operatörler gibi ilkel işlemler üzerindeki genel işlemleri geçersiz kılabileceğinizi de umuyorsunuz. Alt yazı olmadan, gibi operatörler +basit bir makine kodu talimatına kadar derlenebilir. Geçersiz kılınabilirse çalışma zamanında yöntemleri çok daha maliyetli bir şekilde çözmeniz gerekir . (C # 'nın operatör aşırı yüklenmesini desteklediğini biliyor olabilirsiniz - ancak bu aynı değildir. Operatör aşırı yüklenmesi derleme zamanında çözüldü, bu nedenle varsayılan çalışma zamanı cezası yok.)

Dizeler ilkel değildir, ancak bellekte nasıl temsil edildikleri konusunda hala "özeldir". Örneğin, bunlar "interned" dir, yani eşit olan iki karakter değişmezi aynı referans için optimize edilebilir. Dizgi örnekleri de sınıfı takip etmeli, bu mümkün olmaz (ya da en azından çok daha az etkili).

Açıkladığınız şey kesinlikle faydalı olacaktır, ancak onu desteklemek, ilkel ve dizgelerin her kullanımı için, kalıtımdan faydalanmadıklarında bile bir performans yükü gerektirecektir.

Smalltalk dil yapar (sanırım) tamsayılar subclassing izin verir. Ancak Java tasarlandığında, Smalltalk çok yavaş olarak kabul edildi ve her şeyin bir nesne olmasının yükü ana nedenlerden biri olarak kabul edildi. Java, daha iyi performans elde etmek için zarafet ve kavramsal saflığı feda etti.


12
@Den: stringmühürlüdür, çünkü değişmez davranması için tasarlanmıştır. Eğer bir dize miras kalsaydı, gerçekten hataya açık hale getirebilecek değişken dizeler oluşturmak mümkün olurdu. .NET çerçevesi de dahil olmak üzere tonlarca kod, hiçbir yan etkisi olmayan dizgilere dayanır. Burada ayrıca, size aynı şeyi söyler: quora.com/Why-String-class-in-C-is-a-sealed-class
Doc Brown

5
@DocBrown Bu ayrıca Java'da da Stringişaretlenmesinin nedenidir final.
Dev,

47
“Java tasarlandığında, Smalltalk çok yavaş olarak kabul edildi […]. Java daha iyi performans elde etmek için biraz şıklık ve kavramsal saflıktan fedakarlık etti.” - İronik olarak, elbette, Java, Sun'ın Smalltalk VM teknolojisine erişmek için bir Smalltalk şirketini satın alana kadar Sun'ın kendi JVM'sinin köpek yavaş olduğu ve biraz değiştirilmiş bir Smalltalk VM olan HotSpot JVM'yi piyasaya sürmesinden bu yana aslında bu performansı elde etmedi.
Jörg W Mittag

3
@underscore_d: Çok açık bir şekilde ilişkilendirdiğiniz cevap, C♯'nin ilkel tiplerinin olmadığını belirtir . Elbette, bir C♯ uygulaması olan bazı platformlar ilkel tiplere sahip olabilir veya olmayabilir, ancak bu C♯'nin ilkel tiplere sahip olduğu anlamına gelmez. Örneğin, CLI için bir Ruby uygulaması vardır ve CLI ilkel tiplere sahiptir, ancak bu Ruby'nin ilkel tiplere sahip olduğu anlamına gelmez. Uygulama, değer türlerini platformun ilkel türleriyle eşleştirerek uygulamayı seçebilir veya seçemez, ancak bu, özel bir iç uygulama detayıdır ve şartnamenin bir parçası değildir.
Jörg W Mittag

10
Hepsi soyutlama ile ilgili. Başımızı temiz tutmalıyız, yoksa saçma sapan biter. Örneğin: C♯. NET'te uygulanır. .NET, Windows NT'DE uygulanır. Windows NT, x86'da uygulanır. x86, silikon dioksit üzerine uygulanır. SiO₂ sadece kumdur. Yani, bir stringC♯ sadece kum mu? Hayır, elbette değil, stringC in'de bir C♯ belirtiminin söylediği şey budur. Nasıl uygulandığı önemli değil. C♯ bir yerli uygulama ECMAScript uygulaması ECMAScript onları eşlersiniz, bayt dizileri olarak dizeleri uygulayacağını Strings, vb
Jörg W Mittag

20

Bazı dillerin önerdiği şey alt sınıflandırma değil, altyazıdır . Örneğin, Ada türetilmiş türler veya alt türler oluşturmanıza olanak sağlar . Ada Programlama / Türü Sistem bölümü tüm ayrıntıları anlamak okumaya değer. Çoğu zaman istediğiniz şey olan değer aralığını sınırlayabilirsiniz:

 type Angle is range -10 .. 10;
 type Hours is range 0 .. 23; 

Açıkça dönüştürürseniz, her iki türü de Tamsayı olarak kullanabilirsiniz. Bunu da unutmayın olamaz aralıkları (tip adlarıyla kontrol edilir) yapısal olarak eşdeğer olsa bile, başka yerine birini kullanın.

 type Reference is Integer;
 type Count is Integer;

Yukarıdaki tipler aynı değer aralığını temsil etseler de uyumsuzdur.

(Ama Unchecked_Conversion'ı kullanabilirsiniz; size insanlara söylediğimi söyleme)


2
Aslında, bunun anlambilim hakkında daha fazla olduğunu düşünüyorum. Dizinin beklendiği bir miktar kullanılması, umarım derleme zamanı hatasına neden olur
Marjan Venema

@MarjanVenema Yapar ve bu mantık hatalarını yakalamak için bilerek yapılır.
coredump

Demek istediğim, anlambilimi istediğiniz her durumda değil, ürün yelpazesine ihtiyacınız olacaktı. Daha sonra, type Index is -MAXINT..MAXINT;tüm tamsayılar geçerli olacağı için, bir şekilde benim için hiçbir şey yapmadı mı? Öyleyse, kontrol edilenler aralıklarsa bir İndeksine Açı'yı nasıl geçirebilirim?
Marjan Venema

1
@MarjanVenema İkinci örnekte her iki tür de Tam Sayı'nın alt türleridir. Bununla birlikte, bir Sayımı kabul eden bir işlev bildirirseniz, bir Referansı geçemezsiniz, çünkü tür denetimi, "denetlenen her şey aralıklardır" ın tersine olan ad eşdeğerliğine dayanır . Bu tamsayılarla sınırlı değildir, numaralandırılmış türleri veya kayıtları kullanabilirsiniz. ( archive.adaic.com/standards/83rat/html/ratl-04-03.html )
Ağustos'ta

1
@Marjan Etiketleme türlerinin neden oldukça güçlü olabileceğine dair güzel bir örnek, Eric Lippert'in OCaml'deki Zorg'u uygulayan serisinde bulunabilir . Bunu yapmak, derleyicinin birçok hatayı yakalamasını sağlar - diğer yandan, türleri örtük olarak dönüştürmenize izin verirseniz, bu özelliği kullanışsız hale getirir gibi görünüyor. çünkü her ikisi de aynı temeldedir.
Voo,

16

Bunun çok iyi bir X / Y sorusu olabileceğini düşünüyorum. Çıkık noktalar, sorudan ...

Motivasyonum derleme zamanı doğruluğu doğrulaması için tip sisteminin kullanımını en üst düzeye çıkarmak.

... ve yorumlarınızı detaylandırarak:

Birini bir başkasıyla örtük olarak değiştirmek mümkün olmak istemiyorum.

Afedersiniz, eğer bir şeyleri özlüyorum, ama ... Eğer bunlar sizin amacınızsa, neden Dünya'da mirastan bahsediyorsunuz? Örtük yer değiştirebilirlik ... her şey gibi. Bilirsin, Liskov Değişim İlkesi?

İstediğiniz gibi görünen şey, gerçekte, 'güçlü bir typedef' kavramıdır; bu nedenle bir şey 'örneğin intmenzil ve temsil anlamındadır, ancakint bunun tam tersini bekleyen bağlamlarda kullanılamaz. Bu terim hakkında bilgi aramanızı ve seçtiğiniz dil (ler) in ne söyleyebildiğini öneririm. Yine, kelimenin tam anlamıyla mirasın tam tersi.

Ve X / Y cevabını beğenmeyenler için, başlığın LSP'ye atıfta bulunarak hala cevaplanabileceğini düşünüyorum. İlkel türler ilkeldir, çünkü çok basit bir şey yaparlar ve hepsi bu . Miras alınmalarını ve böylece olası etkilerini sınırsız yapmalarını sağlamak, en iyi ihtimalle büyük sürprizlere ve en kötü ölümcül LSP ihlallerine yol açacaktır. Eğer iyimser bir şekilde Thales Pereira’yı benimsemeyi düşünürsem , bu olağanüstü yorumdan alıntı ile beni ilgilendirmez :

Birisi Int'den miras alabilseydi, artık veritabanına bir günlük yazan "int x = y + 2" (burada Y türetilmiş sınıftır) gibi bir masum kodunuz olacaktı. bir şekilde Elvis'i diriltiyorlar. İlkel türlerin güvenli olduğu ve az ya da çok garantili, iyi tanımlanmış bir davranışı olduğu varsayılmaktadır.

Birisi ilkel bir tür görürse, aklı başında bir dilde, haklı olarak her zaman küçük bir şey yapabileceğini, sürprizler olmadan çok iyi olacağını varsayarlar. İlkel türlerin kalıtsal olup olmadıklarını ve yöntemlerinin geçersiz kılındığını bildiren hiçbir sınıf bildirimi yoktur. Öyle olsaydı, gerçekten çok şaşırtıcı olurdu (ve tamamen geriye dönük uyumluluk kırıldı, ancak bunun 'X'in neden Y ile tasarlanmadığını') bunun geriye dönük bir cevabı olduğunu biliyorum.

... Mooing Duck'ın cevaben işaret ettiği gibi, operatörün aşırı yüklenmesine izin veren diller, kullanıcının gerçekten isterlerse kendilerini benzer veya eşit ölçüde karıştırmasını sağlar, bu nedenle bu son argümanın geçerli olup olmadığı şüphelidir. Ve şimdi başkalarının yorumlarını da özetlemeyi bırakacağım.


4

Uygulama tasarımında genellikle oldukça istendiği düşünülen 8 sanal gönderimde kalıtım sağlamak için çalışma zamanı tür bilgisi gerekir. Her nesne için, nesnenin türüne ilişkin bazı verilerin kaydedilmesi gerekir. İlkel, tanım başına bu bilgiden yoksundur.

İlkel özelliklere sahip iki (yönetilen, VM'de çalışan) ana OOP dili vardır: C # ve Java. Diğer pek çok dil ilk başta ilkel değildir veya onları kullanmak / kullanmak için benzer bir akıl yürütme kullanırlar.

İlkeller performans için bir uzlaşmadır. Her nesne için, nesne başlığı için boşluğa ihtiyacınız vardır (Java'da 64 bit VM'lerde genellikle 2 x 8 bayt), artı alanları ve nihayetinde doldurma (Hotspot'ta her nesne, birden fazla olan baytları kaplar) 8). Dolayısıyla intas nesnesinin sadece 4 byte yerine (Java'da) en az 24 byte'a kadar hafızaya ihtiyacı olacaktır.

Böylece, performansı iyileştirmek için ilkel türler eklendi. Bir çok şeyi kolaylaştırıyorlar. a + bHer ikisi de alt tipleri ise ne anlama geliyor int? Doğru ilaveyi seçmek için bir çeşit dağıtma eklenmelidir. Bu sanal gönderme anlamına gelir. Ekleme için çok basit bir opcode kullanma yeteneğine sahip olmak, çok, çok daha hızlıdır ve derleme zamanı optimizasyonlarına izin verir.

Stringbaşka bir dava. Hem Java hem de C #, Stringbir nesnedir. Fakat C # 'da mühürlü, Java da nihai. Çünkü hem Java hem de C # standart kütüphanelerinin Stringdeğişmez olmaları gerekir ve bunları alt sınıflara ayırmak bu değişmezliği kırar.

Java durumunda, VM daha iyi performans elde etmek için intern dizgilerini ("yapar") ve "havuzlayabilir". Bu, yalnızca Dizeler gerçekten değişken olmadığında çalışır.

Ayrıca, nadiren ilkel türlerin alt sınıfına ihtiyaç duyulur. İlkellerin alt sınıfa giremediği sürece, matematiğin bize anlattığı çok sayıda güzel şeyler vardır. Örneğin, eklemenin değişmeli ve birleştirici olduğundan emin olabiliriz. Bu tamsayıların matematiksel tanımının bize söylediği bir şey. Dahası, çoğu durumda indüksiyonla değişmezleri döngüleri kolayca önleyebiliriz. Eğer alt sınıflamaya izin intverirsek, matematiğin bize verdiği araçları kaybediyoruz, çünkü belirli özelliklerin elinde tutulması artık garanti edilemiyor. Böylece, ben yeteneğini derdim değil aslında iyi bir şeydir ilkel türleri alt sınıf yapabilmesi için. Birisi daha az şey kıramaz, ayrıca bir derleyici sık sık belirli optimizasyonlar yapabileceğini ispatlayabilir.


1
Bu cevap uçurum ... dar. to allow inheritance, one needs runtime type information.Yanlış. For every object, some data regarding the type of the object has to be stored.Yanlış. There are two mainstream OOP languages that feature primitives: C# and Java.C ++ şu anda ana akım değil mi? Çalışma zamanı tür bilgisi bir C ++ terim olduğu için onu çürütücü olarak kullanacağım . dynamic_castVeya kullanmadan kesinlikle gerekli değildir typeid. Ve eğer RTTI açık olsa bile , kalıtım sadece bir sınıfın sınıf virtualbaşına bir metot tablosunun gösterilmesi gereken metotlara sahip olması durumunda alan tüketir
underscore_d 21

1
C ++ 'da kalıtım, VM'de çalışan dillerden çok farklı çalışır. sanal gönderim, C ++ 'nın bir parçası olmayan RTTI gerektirir. Sanal gönderimsiz kalıtım çok sınırlıdır ve sanal gönderim ile kalıtımla karşılaştırmanız gerekip gerekmediğinden bile emin değilim. Ayrıca, bir "nesne" kavramı C ++ 'ta çok farklıdır, sonra C # ya da Java' dadır. Haklısın, daha iyi söyleyebileceğim bazı şeyler var, ancak çok ilgili konuların tümüne çabucak girmek, dil tasarımı üzerine bir kitap yazmak zorunda kalmasına neden oluyor.
Polygnome,

3
Ayrıca, C ++ 'da "sanal gönderinin RTTI gerektirmesi" söz konusu değildir. Yine, sadece dynamic_castve buna typeinfoihtiyacım var. Sanal gönderme, nesnenin somut sınıfı için vtable'a bir gösterici kullanılarak pratik olarak uygulanır, böylece doğru işlevlerin çağrılmasına izin verir, ancak RTTI'da bulunan tür ve ilişkinin detayını gerektirmez. Derleyicinin bilmesi gereken tek şey bir nesnenin sınıfının polimorfik olup olmadığı ve eğer öyleyse, örneğin vptr'sinin ne olduğudur. Biri sanal olarak gönderilen sınıfları önemsiz bir şekilde derleyebilir -fno-rtti.
underscore_d

2
Aslında bu, diğer taraftan, RTTI sanal gönderim gerektiriyor. Kelimenin tam anlamıyla -C ++ dynamic_castsanal gönderme olmadan sınıflarda izin vermiyor . Uygulama nedeni, RTTI'nın genellikle bir vtable'ın gizli bir üyesi olarak uygulanmasıdır.
MSalters

1
@MilesRout C ++, en azından biraz daha yeni standartlar için bir dilin OOP için ihtiyaç duyduğu her şeye sahiptir. Birisi, eski C ++ standartlarının bir OOP dili için ihtiyaç duyulan bazı şeylerden yoksun olduğunu, ancak bunun bir gerginlik olduğunu iddia edebilir. C ++ bir değil üst düzey bazı şeyler üzerinde daha doğrudan, düşük seviye kontrolü sağlar, ancak yine de OOP verdiğinden, OOP dili. (Burada soyutlama açısından yüksek seviye / Düşük seviye , yönetilenler gibi diğer diller, C ++ 'dan sonra sistemi daha fazla soyutlar, dolayısıyla soyutlamaları daha yüksektir).
Polygnome

4

Ana akım güçlü statik OOP dillerinde, alt yazma, öncelikle bir türü genişletmenin ve türün geçerli yöntemlerini geçersiz kılmanın bir yolu olarak görülür.

Bunu yapmak için 'nesneler' kendi türüne bir işaretçi içerir. Bu bir ek yük: bir Shapeörneği kullanan bir yöntemdeki kodun, çağrılacak doğru Area()yöntemi bilmeden önce bu örnek türüne erişmesi gerekir .

İlkel, üzerinde yalnızca tek bir makine dili talimatlarına çevrilebilecek ve onlarla herhangi bir tür bilgi taşımayan işlemlere izin verme eğilimindedir. Birinin alt sınıfa girebilmesi için bir tamsayıyı yavaşlatmak, ana dilde olan dilleri durduracak kadar çekici değildi.

Yani cevap:

Ana akım güçlü statik OOP dilleri neden ilkel miras almayı önlüyor?

Dır-dir:

  • Çok az talep vardı
  • Ve dili çok yavaşlatırdı.
  • Alt yazma, öncelikle daha iyi (kullanıcı tanımlı) statik tür kontrolü elde etmenin bir yolundan ziyade bir türü genişletmenin bir yolu olarak görülmüştür.

Bununla birlikte, 'type' dışındaki değişkenlerin özelliklerine göre statik denetime izin veren diller almaya başlıyoruz, örneğin F # "boyut" ve "birim" değerine sahip, örneğin bir alana uzunluk ekleyemezsiniz .

Ayrıca, türün ne yaptığını değiştirmeyen (veya değiştirmeyen) 'kullanıcı tanımlı türlere' izin veren diller de vardır, ancak yalnızca statik tür denetimine yardımcı olur; coredump'ın cevabını görün.


F # ölçü birimleri maalesef yanlış adlandırılmış olmasına rağmen güzel bir özellik. Aynı zamanda sadece derleme zamanıdır, yani derlenmiş bir NuGet paketini tüketirken süper kullanışlı değildir. Doğru yön olsa da.
Den

"Boyut" un "tür" dışında bir mülk "olmadığını, belki alışkın olabileceğinden daha zengin bir tür olduğunu not etmek ilginçtir.
porglezomp

3

Burada bir şeyi gözden geçirip geçirmediğimden emin değilim, ancak cevap oldukça basit:

  1. İlkellerin tanımı şöyledir: ilkel değerler nesne değildir, ilkel türler nesne türleri değildir, ilkel değerler nesne sisteminin bir parçası değildir.
  2. Kalıtım, nesne sisteminin bir özelliğidir.
  3. Ergo, ilkel olamaz miras katılmaktadır.

Hatta hangi gerçekten sadece iki tane güçlü statik cepten dilleri olduğunu unutmayın sahip Java ve C ++: AFAIK ilkeller. (Aslında, ikincisinden bile emin değilim, C ++ hakkında fazla bir şey bilmiyorum ve arama yaparken kafa karıştırıcı olduğunu buldum.)

C ++ 'da, ilkeller temel olarak C'den miras alınan (kaba yönelik) bir mirastır. Bu nedenle, C'nin ne bir nesne sistemi ne de mirası vardır çünkü nesne sisteminde yer almazlar (ve dolayısıyla miras).

Java'da, ilkel ürünler performansı artırma konusundaki yanlış yönlendirilmiş bir girişimin sonucudur. İlkel, aynı zamanda sistemdeki tek değer türleridir, aslında Java'da değer türleri yazmak imkansızdır ve nesnelerin değer türleri olması imkansızdır. Öyleyse, ilkellerin nesne sisteminde yer almaması ve bu nedenle “miras” fikri anlamsız bile değil , onlardan miras kaldıysanız bile , bunu sürdüremezsiniz ”. değer-lık". Bu, örneğin C♯ farklıdır yapar değer türleri (var structyine nesnelerdir s),.

Başka bir şey ise, miras alamamanın aslında ilkellere de özgü olmamasıdır. C♯'de structs dolaylı olarak s 'den miras System.Objectalabilir ve uygulayabilir interface, ancak classes veya structs tarafından miras alınamaz veya miras alınamaz . Ayrıca, sealed classes miras alınamaz. Java'da final classes'ler miras alınamaz.

tl; dr :

Ana akım güçlü statik OOP dilleri neden ilkel miras almayı önlüyor?

  1. ilkel nesneler nesne sisteminin bir parçası değildir (eğer tanımlanmışlarsa, ilkel olmazlardıysa), miras fikri nesne sistemine bağlı, ergo ilkel miras ise bir çelişkidir
  2. ilkel değerler benzersiz değildir, başka birçok tür de kalıtsal olarak alınamaz ( finalveya sealedJava veya C♯, structC♯, case classScala’da es)

3
Ehm ... "C Sharp" olarak telaffuz edildiğini biliyorum, ama, ehm
Bay Lister

Bence C ++ tarafında oldukça yanılıyorsun. Hiç de saf bir OO dili değil. Sınıf yöntemleri varsayılan olarak değildir virtual; bu, LSP'ye uymadıkları anlamına gelir. Örneğin std::string, ilkel değil, ama başka bir değer gibi davranıyor. Bu tür bir anlam semantiği oldukça yaygındır, C ++ 'in STL bölümünün tamamı bunu kabul eder.
MSalters

2
'Java'da, ilkel ürünler performansı artırma konusunda yanlış yönlendirilmiş bir girişimin sonucudur.' İlkeleri uygulamanın kullanıcı tarafından genişletilebilir nesne türleri olarak uygulanmasının yarattığı performansın büyüklüğü hakkında hiçbir fikriniz olmadığını düşünüyorum. Java’daki bu karar hem kasıtlı hem de sağlam bir şekilde oluşturulmuştur. Sadece intkullandığınız her şey için bellek ayırmak zorunda olduğunuzu hayal edin . Her tahsisat 100ns artı çöp toplama ek yükü siparişini alır. İki ilkel ints ekleyerek tüketilen tek CPU döngüsü ile karşılaştırın . Java kodlarınız, dilin tasarımcılarının başka türlü karar vermesi durumunda tarardı.
cmaster

1
@cmaster: Scala'nın ilkelleri yok ve sayısal performansı Java ile tamamen aynı. Çünkü, tamsayıları JVM ilkelleri içinde derler int, bu yüzden aynısını yaparlar. (Scala-native bunları ilkel makine kayıt defterlerinde toplar, Scala.js bunları ilkel ECMAScript Numbers'de toplar .) Ruby'de ilkel yok, ancak YARV ve Rubinius tamsayıları ilkel makine tam sayılarında derliyor, JRuby bunları ilk kez JVM ilkel longs. Hemen hemen her Lisp, Smalltalk veya Ruby uygulama temelögeye kullanan VM . Performans optimizasyonlarının yapıldığı yer…
Jörg W Mittag

1
… Ait: derleyicide, dilde değil.
Jörg W Mittag

2

“Etkili Java” daki Joshua Bloch, kalıtım için açıkça tasarlamayı veya yasaklamayı önerir. İlkel sınıflar miras için tasarlanmamıştır, çünkü bunlar değişmez olacak şekilde tasarlanmıştır ve mirasa izin verilmesi, alt sınıflarda ki Liskov prensibini kırar ve birçok hata kaynağı olur.

Her neyse, bu neden kötü bir geçici çözüm? Miras yerine kompozisyonu gerçekten tercih etmelisin. Sebep sizin performansınızdan daha önemliyse ve sorunuzun cevabı Java'da tüm özellikleri koymanın mümkün olmamasıdır, çünkü özellik eklemenin tüm farklı yönlerini analiz etmek zaman alır. Örneğin, Java 1.5'ten önce Generics'e sahip değildi.

Çok fazla sabrınız varsa, şanslısınız çünkü Java'ya değer sınıfları eklemeyi planlayan bir değer vardır, bu da performansı artırmanıza yardımcı olacak değer sınıflarınızı oluşturmanıza izin verir ve aynı zamanda size daha fazla esneklik sağlar.


2

Soyut düzeyde, istediğiniz bir şeyi tasarladığınız dilde ekleyebilirsiniz.

Uygulama düzeyinde, bazı şeylerin uygulanmasının daha kolay olacağı, bazılarının karmaşık olacağı, bazılarının hızlı hale getirilebileceği, bazılarının daha yavaş olma zorunluluğunu duymaları kaçınılmazdır. Bunu hesaba katmak için, tasarımcıların genellikle zor kararlar vermeleri ve ödün vermeleri gerekir.

Uygulama düzeyinde, bir değişkene erişmenin en hızlı yollarından biri adresini bulmak ve bu adresin içeriğini yüklemektir. Çoğu CPU'da adreslerden veri yüklemek için özel talimatlar vardır ve bu talimatlar genellikle kaç bayt yüklenmeleri gerektiğini (bir, iki, dört, sekiz vb.) Ve yükledikleri verileri nereye koyacaklarını bilmelidir (tek kayıt, kayıt çift, genişletilmiş kayıt, diğer hafıza vb. Bir değişkenin boyutunu öğrenerek, derleyici bu değişkenin kullanımları için tam olarak hangi talimatın yayılacağını bilir. Bir değişkenin boyutunu bilmeden, derleyicinin daha karmaşık ve muhtemelen daha yavaş bir şeye başvurması gerekir.

Soyut düzeyde, alt yazmanın amacı, eşit veya daha genel bir türün beklendiği bir türden örnekleri kullanabilmektir. Başka bir deyişle, belirli bir türdeki bir nesneyi veya daha fazla türetilmiş herhangi bir şeyi bekleyen, tam olarak bunun ne olacağını bilmeden, kod yazılabilir. Açıkçası, daha fazla türetilmiş tür daha fazla veri üyesi ekleyebildiği için, türetilmiş bir türün temel türleriyle aynı bellek gereksinimine sahip olması gerekmez.

Uygulama düzeyinde, önceden belirlenmiş bir büyüklükteki bir değişkenin bilinmeyen bir büyüklük örneğini tutması ve normalde verimli dediğiniz bir şekilde erişilmesi için basit bir yolu yoktur. Ancak, işleri biraz hareket ettirmenin ve nesneyi saklamak için değil, nesneyi tanımlamak ve o nesnenin başka bir yerde saklanmasına izin vermek için bir değişken kullanmanın bir yolu vardır. Bu bir referanstır (örn. Bir hafıza adresi) - bir değişkenin nesneyi bu bilgiler boyunca bulabildiğimiz sürece sadece bir tür sabit boyutlu bilgiyi tutması gereken ekstra bir dolaylı yönlendirmedir. Bunu başarmak için, sadece adresi yüklemeliyiz (sabit boyutta) ve o zaman bildiğimiz ofsetlerde daha fazla veri olsa bile, geçerli olduğunu bildiğimiz nesnenin ofsetlerini kullanarak her zamanki gibi çalışabiliriz. Bunu yapabiliriz çünkü yapmıyoruz.

Soyut düzeyde, bu yöntem, bir (a referansına) referansını , onu yapan bilgiyi kaybetmeden stringbir objectdeğişkene kaydetmenizi sağlar string. Her türün bu şekilde çalışması iyidir ve ayrıca birçok açıdan zarif olduğunu söyleyebilirsiniz.

Yine de, uygulama düzeyinde, fazladan dolaylı yönlendirme daha fazla talimat içerir ve çoğu mimaride nesneye erişimi daha da yavaşlatır. Dilinizde, bu fazladan bir dolaylı aktarım seviyesine sahip olmayan, sık kullanılan bazı türleri (referans) eklerseniz, derleyicinin bir programdan daha fazla performans elde etmesine izin verebilirsiniz. Ancak, derleyici seviyesini kaldırarak, derleyici artık bellekte güvenli bir şekilde alt yazı yazmanıza izin veremez. Bunun nedeni, türünüze daha fazla veri üyesi eklerseniz ve daha genel bir türe atarsanız, hedef değişken için ayrılan alana sığmayan fazladan veri üyesi kesilir.


1

Genel olarak

Bir sınıf soyut ise (metafor: delikli bir kutu), "delik (ler) i doldurmak" şarttır (kullanılabilir bir şey olması gerekir!), Bu yüzden soyut sınıfları alt sınıflandırırız.

Bir sınıf somutsa (metafor: dolu bir kutu), mevcut olanı değiştirmek uygun değildir çünkü eğer dolusa, dolu. Kutuya daha fazla bir şeyler eklemek için yerimiz yok, bu yüzden somut sınıfları alt sınıflamamalıyız.

İlkellerle

İlkel tasarımdan somut sınıflardır. İyi bilinen, tamamen kesin olan bir şeyi temsil ederler (soyut olan ilkel bir tür görmedim, aksi halde artık ilkel değildir) ve sistemde yaygın olarak kullanılırlar. İlkel bir türü alt sınıflara ayırma ve ilkellerin tasarlanmış davranışına dayanan başkalarına kendi uygulamanızı sağlama izni, birçok yan etkiye ve büyük hasarlara neden olabilir!



Bağlantı ilginç bir tasarım görüşüdür. Benim için daha fazla düşünmeye ihtiyacı var.
Den

1

Genelde miras, istediğiniz anlambilim değildir, çünkü özel türünüzü ilkel beklenen bir yerle değiştiremezsiniz. Örneğinizden ödünç almak için, Quantity + Indexanlamsal olarak bir anlam ifade etmiyor, bu nedenle miras ilişkisi yanlış ilişki.

Ancak, birkaç dilde , tanımladığınız ilişki türünü ifade eden bir değer türü kavramı vardır. Scala bir örnektir. Bir değer türü, temel temsil olarak ilkel kullanır, ancak dışarıda farklı bir sınıf kimliğine ve işlemlerine sahiptir. Bu, ilkel bir türü genişletme etkisine sahiptir, ancak miras ilişkisi yerine daha çok bir kompozisyondur.

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.