Java'nın Koleksiyon arayüzünü kullanmak için iyi bir neden var mı?


11

Bu arayüzün belirli bir uygulamasına bağlı kalmamak için mevcut en genel arayüzü kullanmanız gerektiği argümanını duydum. Bu mantık java.util.Collection gibi arayüzler için geçerli mi?

Aşağıdaki gibi bir şey görmek istiyorum:

List<Foo> getFoos()

veya

Set<Foo> getFoos()

onun yerine

Collection<Foo> getFoos()

Son durumda, ne tür veri seti ile uğraştığımı bilmiyorum, oysa ilk iki durumda sipariş ve teklik hakkında bazı varsayımlar yapabilirim. Java.util.Collection öğesinin hem kümeler hem de listeler için mantıksal bir üst öğe olmasının dışında bir faydası var mı ?

Bir kod incelemesi yaparken Koleksiyon'u kullanan bir kodla karşılaşırsanız, kullanımının haklı olup olmadığını nasıl belirlersiniz ve daha spesifik bir arayüzle değiştirilmesi için hangi önerileri yaparsınız?


3
Java.security.cert dosyasında bir dönüş türünün olduğunu fark ettiniz Collection<List<?>>mi? Korku kodlama hakkında konuşun!
Macneil

1
@Macneil Hangi sınıfa atıfta bulunduğumu bilmiyorum, ama böyle bir dönüş türü gerçekten oldukça mantıklı olabilir. Bu temelde bir olduğunu anlatır koleksiyonu (yani bir sürü şey ihtiva yok bir mantıklı sipariş) listeleri (yani şeyler içeren var bir mantıklı sipariş) nesneler (kimin türleri biz statik olarak bilmiyoruz yani ürün ne sebeple olursa olsun). Bana mantıksız gelmiyor.
Sıfır3

Yanıtlar:


13

Soyutlamalar uygulamalardan daha uzun yaşar

Genel olarak tasarımınız ne kadar soyut olursa faydalı olma ihtimali de o kadar uzun olur. Dolayısıyla, Koleksiyon alt arayüzlerden daha soyut olduğu için, Koleksiyona dayalı bir API tasarımının Liste'ye dayalı bir API tasarımından daha yararlı olma olasılığı daha yüksektir.

Ancak, kapsayıcı prensip en uygun soyutlamayı kullanmaktır . Bu nedenle, koleksiyonunuzun sipariş edilen öğeleri desteklemesi gerekiyorsa, bir Liste zorunlu kılınacaksa, kopyalar yoksa bir Set zorunlu kılınması vb.

Genel arayüz tasarımı hakkında bir not

Koleksiyon arayüzünü jeneriklerle kullanmakla ilgilendiğiniz için aşağıdakileri yardımcı olabilirsiniz. Etkili Java by Joshua Bloch , jeneriklere dayanacak bir arayüz tasarlarken aşağıdaki yaklaşımı önerir: Producers Extend, Consumers Super

Bu aynı zamanda PECS kuralı olarak da bilinir . Temel olarak, veri üreten genel koleksiyonlar sınıfınıza aktarılırsa imza şu şekilde görünmelidir:

public void pushAll(Collection<? extends E> producerCollection) {}

Böylece giriş türü E veya E'nin herhangi bir alt sınıfı olabilir (E, Java dilinde kendisinin hem üst hem de alt sınıfı olarak tanımlanır).

Tersine, veri tüketmek için geçirilen genel bir koleksiyonun aşağıdaki gibi bir imzası olmalıdır:

public void popAll(Collection<? super E> consumerCollection) {}

Yöntem, E'nin herhangi bir süper sınıfıyla doğru bir şekilde ilgilenecektir. Genel olarak, bu yaklaşımı kullanmak arayüzünüzü kullanıcılarınız için daha az şaşırtıcı hale getirecektir, çünkü geçebileceksiniz Collection<Number>ve Collection<Integer>doğru şekilde muamele edebileceksiniz .


6

CollectionArayüz ve en müsamahakar formu Collection<?>için mükemmeldir parametreleri kabul söyledi. Dayanarak Java kütüphanesinde kendisi kullanılmak bu dönüş türü daha parametre türü olarak daha yaygındır.

Geri dönüş türleri için, puanınızın geçerli olduğunu düşünüyorum: İnsanların buna erişmesi bekleniyorsa, yapılan işlemin sırasını (Büyük-O anlamda) bilmeleri gerekir. Döndüğümden tekrarlayıp Collectionbaşka bir Koleksiyona eklerdim, ancak containsbir O (1), O (log n) veya O (n) işlemi olup olmadığını bilmeden, çağırmak biraz çılgınca görünecektir . Tabii ki, sadece Setbir hashset veya sıralı bir kümeye sahip olduğunuz anlamına gelmez, ancak bir noktada arayüzün makul bir şekilde uygulandığını varsayarsınız (ve sonra varsayımınız varsa B planına gitmeniz gerekir. yanlış olduğu gösterilmiştir).

Tom'un da belirttiği gibi, Collectionkapsüllemeyi sürdürmek için bazen bir geri göndermeniz gerekir : Daha spesifik bir şey döndürebilseniz bile uygulama ayrıntılarının sızdırılmasını istemezsiniz. Ya da Tom'un bahsettiği durumda, daha spesifik bir konteyneri iade edebilirsiniz, ancak daha sonra onu inşa etmeniz gerekir.


2
İkinci noktanın biraz zayıf olduğunu düşünüyorum. Koleksiyonun, Koleksiyon veya Liste'den bağımsız olarak nasıl performans göstereceğini bilmiyorsunuz - çünkü bunlar sadece soyutlamalar. Somut bir final sınıfınız yoksa, gerçekten söyleyemezsiniz.
Mark H

Ayrıca, tek bildiğiniz bir şey bir Koleksiyon ise, kopyalar içerip içermediğine dair hiçbir fikriniz yoktur. Geri dönüp, Koleksiyonu geri döndürmenin uygun olabileceği bir durumda, kopya içermeyen ve önemli bir düzeni olmayan (doğal olarak bir Set olacaktır), ancak iyi bir nedenden dolayı, dönen yöntemin uygulanması bir koleksiyonunuz varsa bir Liste kullanır. Bir Listeyi döndürmek istemezsiniz, çünkü bu önemli bir sipariş anlamına gelir, ancak bir Set yapmanın zorluklarından geçmeden bir Set'e geri dönemezsiniz. Yani bir Koleksiyon geri dönüyorsun.
Tom Anderson

@ Tom: İyi bir nokta!
Kasım'da Macneil

5

Buna tam ters açıdan bakıp şunu sorardım:

Bir kod incelemesi yaparken List <> öğesini kullanan bir kodla karşılaşırsanız, kullanımının haklı olup olmadığını nasıl belirlersiniz?

Bunu haklı çıkarmak oldukça kolay. Bir Koleksiyon tarafından sunulmayan bazı işlevlere ihtiyaç duyduğunuzda Listeyi kullanırsınız. Bu ekstra işlevselliğe ihtiyacınız yoksa - hangi gerekçeniz var? (Ve satın almayacağım, "görmeyi tercih ederim")

Bir koleksiyonu salt okunur amaçlar için kullanacağınız, hepsini aynı anda dolduracağınız, tamamen yineleyeceğiniz birçok durum vardır - herhangi bir şeyi manuel olarak dizine eklemeniz gerekir mi?

Gerçek bir örnek vermek gerekirse. Diyelim ki bir veritabanında basit bir sorgu gerçekleştiriyorum. ( SELECT id,name,rep FROM people WHERE name LIKE '%squared') İlgili verileri alıyorum, Person nesnelerini dolduruyorum ve bunları bir PersonList'e yerleştiriyorum)

  • Dizine göre erişmem gerekiyor mu? - Anlamsız olurdu. Dizin ve kimlik arasında eşleme yok.
  • Bir dizine eklemem gerekir mi? - Hayır, DBMS nereye koyacağımıza karar verecek.

Peki bu ekstra yöntemler için ne gibi gerekçelerim olurdu? (yine de PersonList'imde uygulanmamış bırakılır)


Doğru tespit. Sorumun bir kod incelemesi yaparken, koleksiyonları döndüren DAO'ları görmeye devam ettiğim belirli bir örneği ifade ediyor, ancak bu DAO çağrılarının her zaman varlık kümeleri döndüreceğini biliyorum; benim görüşüm, bu durumlarda dönüş türünün benzersiz olduğunu gösterir ve bu bilgi bu yöntemi kullanmak zorunda olan herkes için yararlıdır (örneğin, yinelenenleri kontrol etmek zorunda değilim).
Fil

Bir DB'yi sorguladıysanız, sonuçta elde edilen 2 nesneyi eşittir () ile karşılaştırmak hiç doğru üretmemelidir - bu nedenle nesneleri çoğaltma için karşılaştırmanın başka bir yoluna ihtiyacınız olacaktır (ör. Aynı ada, aynı kimliğe, her ikisi de?). DAO'nuza kopyaların kendisini kaldırmak için bunları nasıl karşılaştıracağınızı söylemeniz gerekir - ancak kopyaların var olup olmadığına karar veren kullanıcı olduğunuzdan - bunu arama kodundan koleksiyonla yapmak daha kolaydır. (DAO'ya gezegende olası her eşitlik kontrolünün nasıl yapılacağını bildirmek için daha fazla soyutlama katmanından kaçınmak için.)
Mark H

Kabul etti, ancak Hazırda Bekletme özelliğini kullanıyoruz ve varlıklarımızın eşittir () olmasını sağladık. Dolayısıyla, bir DAO varlıkları döndürürken, çok hızlı bir şekilde yeni HashSet (). AddAll (sonuç) yapabilir ve bunu çağrılan yönteme geri döndürebiliriz.
Fil
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.