Özellikler yerine soyut sınıflar kullanmanın avantajı nedir?


371

Özellik yerine soyut bir sınıf kullanmanın avantajı nedir (performans dışında)? Soyut sınıfların çoğu durumda özelliklerle değiştirilebileceği anlaşılmaktadır.

Yanıtlar:


371

İki fark düşünebilirim

  1. Soyut sınıflar yapıcı parametrelerine ve tip parametrelerine sahip olabilir. Özelliklerin yalnızca tür parametreleri olabilir. Gelecekte özelliklerin bile yapıcı parametrelerine sahip olabileceği konusunda bazı tartışmalar vardı
  2. Soyut sınıflar Java ile tamamen birlikte çalışabilir. Bunları herhangi bir sarmalayıcı olmadan Java kodundan çağırabilirsiniz. Özellikler, yalnızca herhangi bir uygulama kodu içermiyorsa tam olarak birlikte çalışabilir

172
Çok önemli zeyilname: Bir sınıf birden çok özelliğinden miras alabilir, sadece bir soyut sınıf olabilir. Bu, bir geliştiricinin neredeyse tüm durumlarda hangisini kullanacağını sorduğunda sorduğu ilk soru olması gerektiğini düşünüyorum.
BAR

15
cankurtaran: "Özellikler herhangi bir uygulama kodu içermiyorsa tamamen birlikte çalışabilir"
Walrus the Cat

2
özet - bir nesneyi (nesnenin dalını) tanımlayan veya ona yol açan ancak hala (hazır) nesne olarak oluşturulmamış toplu davranışlar. Özellikler, yetenekleri indüklemeniz gerektiğinde, yani yetenekler asla nesnenin yaratılmasından kaynaklanmazsa, bir nesne izolasyondan çıktığında ve iletişim kurması gerektiğinde gelişir veya gereklidir.
Ramiz Uddin

5
İkinci fark Java8'de mevcut değil.
Duong Nguyen

14
Scala 2.12 uyarınca, bir özellik bir Java 8 arayüzüne derlenir - scala-lang.org/news/2.12.0#traits-compile-to-interfaces .
Kevin Meredith

209

Scala'da Programlama'da "Sürekli mi yoksa sürekli mi?" bu soruyu ele alıyor. 1st ed çevrimiçi kullanılabilir olduğundan, burada her şeyi alıntı yapmanın sorun olmadığını umuyorum. (Herhangi bir ciddi Scala programcısı kitabı satın almalıdır):

Yeniden kullanılabilir bir davranış koleksiyonu uyguladığınızda, bir özellik mi yoksa soyut bir sınıf mı kullanmak istediğinize karar vermeniz gerekir. Kesin bir kural yoktur, ancak bu bölüm dikkate alınması gereken birkaç kural içermektedir.

Davranış tekrar kullanılmayacaksa , onu somut bir sınıf haline getirin. Sonuçta yeniden kullanılabilir bir davranış değildir.

İlişkisiz birden çok sınıfta yeniden kullanılabiliyorsa , onu bir özellik haline getirin. Sınıf hiyerarşisinin farklı bölümlerine yalnızca özellikler karıştırılabilir.

Java kodundan miras almak istiyorsanız soyut bir sınıf kullanın. Kodlu özelliklerin Java analogu yakın olmadığından, Java sınıfındaki bir özellikten miras almak garip olma eğilimindedir. Bu arada, bir Scala sınıfından miras almak bir Java sınıfından miras almak gibidir. Bir istisna olarak, yalnızca soyut üyelere sahip bir Scala özelliği doğrudan bir Java arayüzüne dönüşür, bu nedenle Java kodunun miras almasını bekleseniz bile bu özellikleri tanımlamaktan çekinmeyin. Java ve Scala ile birlikte çalışma hakkında daha fazla bilgi için Bölüm 29'a bakın.

Derlenmiş biçimde dağıtmayı planlıyorsanız ve dış grupların kendisinden miras alan sınıflar yazmasını bekliyorsanız, soyut bir sınıf kullanmaya eğilebilirsiniz. Sorun, bir özellik bir üye kazandığında veya kaybettiğinde, kendisinden miras kalan tüm sınıfların, değişmemiş olsalar bile yeniden derlenmesi gerektiğidir. Dış istemciler davranıştan miras almak yerine yalnızca davranışı çağırırlarsa, bir özellik kullanmak iyidir.

Verimlilik çok önemliyse , sınıf kullanmaya yönelin. Çoğu Java çalışma zamanı, bir sınıf üyesinin sanal yöntem çağrılmasını bir arabirim yöntemi çağrısından daha hızlı bir işlem yapar. Özellikler arayüzlere derlenir ve bu nedenle hafif bir performans yükü ödeyebilir. Bununla birlikte, bu seçimi sadece söz konusu özelliğin bir performans darboğazı oluşturduğunu ve bunun yerine bir sınıf kullanmanın aslında sorunu çözdüğüne dair kanıtınız varsa yapmalısınız.

Hala bilmiyorsanız , yukarıdakileri düşündükten sonra, bir özellik olarak yapmaya başlayın. Daha sonra istediğiniz zaman değiştirebilirsiniz ve genel olarak bir özellik kullanmak daha fazla seçeneği açık tutar.

@Mushtaq Ahmed'in belirttiği gibi, bir özellik bir sınıfın birincil kurucusuna iletilen herhangi bir parametreye sahip olamaz.

Başka bir fark, tedavisidir super.

Sınıflar ve özellikler arasındaki diğer fark, sınıflarda superçağrıların statik olarak bağlı olmasına rağmen, özelliklerde dinamik olarak bağlı olmalarıdır. Eğer yazarsanız super.toStringbir sınıfta, sen çağrılır hangi yöntemin uygulanması iyi biliyorum. Ancak aynı özelliği bir özelliğe yazdığınızda, özelliği tanımladığınızda süper çağrı için çağrılacak yöntem uygulaması tanımsız olur.

Daha fazla ayrıntı için Bölüm 12'nin geri kalanına bakın.

Düzenleme 1 (2013):

Soyut sınıfların davranış biçiminde özelliklerle karşılaştırıldığında küçük bir fark vardır. Doğrusallaştırma kurallarından biri, sınıfların kalıtım hiyerarşisini koruduğu, bu da soyut sınıfları zincirde daha sonra itme eğilimi gösterirken, özellikler mutlu bir şekilde karıştırılabilir. Bazı durumlarda, aslında sınıf doğrusallaştırmasının ikinci konumunda olması tercih edilir bunun için soyut sınıflar kullanılabilir. Görmek . Scala'daki kısıtlayıcı sınıf doğrusallaştırması (mixin sırası) .

Düzenleme 2 (2018):

Scala 2.12 itibariyle, trait'in ikili uyumluluk davranışı değişmiştir. 2.12'den önce, bir özelliğin üye eklenmesi veya kaldırılması, sınıflar değişmemiş olsa bile, özelliği devralan tüm sınıfların yeniden derlenmesini gerektiriyordu. Bunun nedeni özelliklerin JVM'de kodlanma biçimidir.

Scala 2.12 itibariyle, özellikler Java arayüzlerine derlendiğinden, gereksinim biraz rahatladı. Özellik aşağıdakilerden herhangi birini yaparsa, alt sınıfları yine de yeniden derleme gerektirir:

  • alanları tanımlama ( valveya varbir sabit tamam - final valsonuç türü olmadan)
  • çağrı super
  • gövdedeki başlatıcı ifadeleri
  • bir sınıfı genişletmek
  • sağ üst bölgede uygulamaları bulmak için doğrusallaştırmaya güvenmek

Ancak özellik yapmazsa, artık ikili uyumluluğu bozmadan güncelleyebilirsiniz.


2
If outside clients will only call into the behavior, instead of inheriting from it, then using a trait is fine- Birisi buradaki farkın ne olduğunu açıklayabilir mi? extendsvs with?
0fnt

2
@ 0fnt Onun farkı, vs ile uzanır hakkında değildir. Söylediği şey, eğer sadece aynı derleme içindeki özelliğe karışırsanız, ikili uyumluluk sorunlarının geçerli olmadığıdır. Bununla birlikte, API'niz kullanıcıların özelliklerin kendilerine karışmasına izin verecek şekilde tasarlanmışsa, ikili uyumluluk konusunda endişelenmeniz gerekir.
John Colanduoni

2
@ 0fnt: extendsve arasında kesinlikle bir anlamsal fark yoktur with. Tamamen sözdizimseldir. Birden fazla şablondan miras alırsanız, birincisi alır extend, diğerleri alır , hepsi withbu. withVirgül olarak düşün class Foo extends Bar, Baz, Qux.
Jörg W Mittag

77

Ne kadar değerli olursa olsun, Odersky ve arkadaşlarının Scala'daki Programlaması, şüpheniz olduğunda özellikleri kullanmanızı önerir. Daha sonra gerektiğinde bunları soyut sınıflara dönüştürebilirsiniz.


20

Birden fazla soyut sınıfı doğrudan genişletememeniz, ancak birden çok özelliği bir sınıfa karıştırabilmeniz dışında, bir özellikteki süper çağrılar dinamik olarak bağlı olduğundan (daha önce karıştırılmış bir sınıf veya özellikten bahsediyorsa) özelliklerin istiflenebilir olduğunu belirtmek gerekir. mevcut olan).

Thomas'ın Soyut Sınıf ve Özellik Arasındaki Farktaki cevabından :

trait A{
    def a = 1
}

trait X extends A{
    override def a = {
        println("X")
        super.a
    }
}  


trait Y extends A{
    override def a = {
        println("Y")
        super.a
    }
}

scala> val xy = new AnyRef with X with Y
xy: java.lang.Object with X with Y = $anon$1@6e9b6a
scala> xy.a
Y
X
res0: Int = 1

scala> val yx = new AnyRef with Y with X
yx: java.lang.Object with Y with X = $anon$1@188c838
scala> yx.a
X
Y
res1: Int = 1

9

Soyut bir sınıfı genişletirken, bu alt sınıfın benzer bir tür olduğunu gösterir. Bence özellikleri kullanırken bu gerekli değildir.


Bunun herhangi bir pratik etkisi var mı, yoksa yalnızca kodun anlaşılmasını kolaylaştırıyor mu?
Ralf

8

Gelen Programlama Scala yazarlar soyut sınıflar klasik nesne yönelimli "is-a" ilişkisi özellikleri kompozisyonun bir Scala-yönlü iken yapmak söylüyorlar.


5

Soyut sınıflar davranış içerebilir - Yapıcı argümanlarıyla (bu özelliklerin yapamayacağı) parametreleştirilebilir ve çalışan bir varlığı temsil edebilir. Bunun yerine özellikler tek bir özelliği, tek bir işlevin arayüzünü temsil eder.


8
Umarım özelliklerin davranış içeremeyeceğini ima etmezsiniz. Her ikisi de uygulama kodu içerebilir.
Mitch Blevins

1
@Mitch Blevins: Elbette hayır. Kod içerebilirler, ancak trait Enumerableçok sayıda yardımcı işlevle tanımladığınızda , onlara davranış demezdim, sadece bir özellikle bağlantılı işlevler.
Dario

4
@Dario "Davranış" ve "işlevsellik" kelimesinin eş anlamlı olduğunu görüyorum, bu yüzden cevabınızı çok kafa karıştırıcı buluyorum.
David J.

3
  1. Bir sınıf birden çok özelliğinden miras alabilir ancak yalnızca bir soyut sınıf olabilir.
  2. Soyut sınıflar yapıcı parametrelerine ve tip parametrelerine sahip olabilir. Özelliklerin yalnızca tür parametreleri olabilir. Örneğin, t (i: Int) {}; i parametresi geçersiz.
  3. Soyut sınıflar Java ile tamamen birlikte çalışabilir. Bunları herhangi bir sarmalayıcı olmadan Java kodundan çağırabilirsiniz. Özellikler, yalnızca herhangi bir uygulama kodu içermiyorsa, tam olarak birlikte çalışabilir.
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.