Bir ImmutableMap veya bir Harita döndürmek daha mı iyi?


90

Diyelim ki Harita döndürmesi gereken bir yöntem yazıyorum . Örneğin:

public Map<String, Integer> foo() {
  return new HashMap<String, Integer>();
}

Bir süre düşündükten sonra, bu haritayı oluşturulduktan sonra değiştirmek için hiçbir neden olmadığına karar verdim. Bu nedenle, bir ImmutableMap döndürmek istiyorum .

public Map<String, Integer> foo() {
  return ImmutableMap.of();
}

Dönüş türünü genel bir Harita olarak bırakmalı mıyım yoksa bir ImmutableMap döndürdüğümü belirtmeli miyim?

Bir taraftan, arayüzlerin tam olarak neden yaratıldığı; uygulama ayrıntılarını gizlemek için.
Öte yandan, bunu böyle bırakırsam, diğer geliştiriciler bu nesnenin değişmez olduğu gerçeğini gözden kaçırabilir. Böylece, değişmez nesneler gibi büyük bir hedefe ulaşamayacağım; değişebilecek nesnelerin sayısını en aza indirerek kodu daha net hale getirmek için. Daha da kötüsü, bir süre sonra birisi bu nesneyi değiştirmeye çalışabilir ve bu bir çalışma zamanı hatasıyla sonuçlanır (Derleyici bu konuda uyarmaz).


2
Birinin sonunda bu soruyu tartışmalar için fazla açık olarak etiketleyeceğinden şüpheleniyorum. "WDYT" yerine daha spesifik sorular sorabilir misiniz? ve "tamam mı ...?" Örneğin, "normal bir haritayı döndürmekle ilgili herhangi bir sorun veya düşünceniz var mı?" ve "hangi alternatifler var?"
kül

Ya daha sonra gerçekten değiştirilebilir bir harita döndürmesi gerektiğine karar verirseniz?
user253751

3
@immibis lol veya bunun için bir Liste?
djechlin

3
API'nize gerçekten bir Guava bağımlılığı oluşturmak istiyor musunuz? Geriye dönük uyumluluğu bozmadan bunu değiştiremezsiniz.
user2357112 Monica'yı destekliyor

1
Bence soru çok kabataslak ve birçok önemli ayrıntı eksik. Örneğin, Guava'ya zaten (görünür) bir bağımlılık olarak sahip olup olmadığınız. Zaten sahip olsanız bile, kod parçacıkları sahte koddur ve gerçekte ulaşmak istediğiniz şeyi aktarmazlar. Kesinlikle olur değil yazma return ImmutableMap.of(somethingElse). Bunun yerine, ImmutableMap.of(somethingElse)bir alan olarak depolar ve yalnızca bu alanı döndürürsünüz. Bütün bunlar buradaki tasarımı etkiliyor.
Marco13

Yanıtlar:


70
  • Halka açık bir API yazıyorsanız ve bu değişmezlik tasarımınızın önemli bir yönü ise, ya yöntemin adının döndürülen haritanın değişmez olacağını açıkça belirtmesini sağlayarak ya da somut türden harita. Javadoc'ta bundan bahsetmek bence yeterli değil.

    Görünüşe göre Guava uygulamasını kullandığınız için, dokümana baktım ve bu soyut bir sınıf, bu yüzden size gerçek, somut tipte biraz esneklik sağlıyor.

  • Dahili bir araç / kitaplık yazıyorsanız, düz bir düzenin döndürülmesi çok daha kabul edilebilir hale gelir Map. İnsanlar aradıkları kodun iç kısımlarını bilecek veya en azından ona kolayca erişebilecekler.

Benim sonucum, açıkça iyidir, işleri şansa bırakmayın.


1
Bir ekstra arayüz, bir ekstra soyut sınıf ve bu soyut sınıfı genişleten bir sınıf. Bu, OP'nin sorduğu gibi basit bir şey için çok fazla güçlüktür, ki bu yöntem, Maparayüz tipi veya ImmutableMapuygulama tipini döndürürse .
fps

20
Guava'ya atıfta bulunuyorsanız ImmutableMap, Guava ekibinin tavsiyesi ImmutableMapsemantik garantili bir arayüz düşünmeniz ve bu türü doğrudan iade etmenizdir.
Louis Wasserman

1
@LouisWasserman mm, sorunun Guava uygulamasına bir bağlantı verdiğini görmedi. O zaman parçacığı kaldıracağım, teşekkür ederim
Dici

3
Louis'e katılıyorum, eğer değişmez bir nesne olan bir harita döndürmek istiyorsanız, dönüş nesnesinin tercihen o türden olmasını sağlayın. Herhangi bir nedenle bunun değişmez bir tür olduğu gerçeğini gizlemek istiyorsanız, kendi arayüzünüzü tanımlayın. Harita olarak bırakmak yanıltıcıdır.
WSimpson

1
@WSimpson Cevabımın söylediği şeyin bu olduğuna inanıyorum. Louis, eklediğim bazı parçalardan bahsediyordu, ama onu kaldırdım.
Dici

38

Sen olmalıdır ImmutableMapiade türü olarak. Mapuygulanması tarafından desteklenmeyen yöntemleri içeren ImmutableMap(örneğin put) ve işaretlenmiştir @deprecatediçinde ImmutableMap.

Kullanımdan kaldırılmış yöntemleri kullanmak, derleyici uyarısına neden olur ve çoğu IDE, insanlar kullanımdan kaldırılmış yöntemleri kullanmaya çalıştığında uyarır.

Bu gelişmiş uyarı, bir şeylerin yanlış olduğuna dair ilk ipucunuz olarak çalışma zamanı istisnalarına sahip olmaya tercih edilir.


1
Bu bence en mantıklı cevap. Harita arayüzü bir sözleşmedir ve ImmutableMap açıkça bunun önemli bir bölümünü ihlal etmektedir. Bu durumda Haritayı bir dönüş türü olarak kullanmak mantıklı değildir.
TonioElGringo

@TonioElGringo peki ya Collections.immutableMapo zaman?
OrangeDog

@TonioElGringo, ImmutableMap'in uygulamadığı yöntemlerin, docs.oracle.com/javase/7/docs/api/java/util/… arayüz belgelerinde açıkça isteğe bağlı olarak işaretlendiğini lütfen unutmayın . Bu nedenle, bir Harita döndürmenin hala mantıklı olacağını düşünüyorum (uygun javadocs ile). Yine de, yukarıda tartışılan nedenlerden dolayı ImmutableMap'i açıkça iade etmeyi seçiyorum.
AMT

3
@TonioElGringo hiçbir şeyi ihlal etmemektedir, bu yöntemler isteğe bağlıdır. İn gibi List, garantisi yoktur List::addgeçerlidir. Deneyin Arrays.asList("a", "b").add("c");. Çalışma zamanında başarısız olacağına dair bir gösterge yok, ancak yine de yapıyor. En azından Guava seni uyarıyor.
njzk2

14

Öte yandan, bunu böyle bırakırsam, diğer geliştiriciler bu nesnenin değişmez olduğu gerçeğini gözden kaçırabilir.

Bunu javadocs'ta belirtmelisiniz. Geliştiriciler onları okur, bilirsiniz.

Böylece, değişmez nesneler gibi büyük bir hedefe ulaşamayacağım; değişebilecek nesnelerin sayısını en aza indirerek kodu daha net hale getirmek için. Daha da kötüsü, bir süre sonra birisi bu nesneyi değiştirmeye çalışabilir ve bu bir çalışma zamanı hatasıyla sonuçlanır (Derleyici bu konuda uyarmaz).

Hiçbir geliştirici test edilmemiş kodunu yayınlamaz. Ve test ettiğinde, sadece sebebini değil, aynı zamanda değişmez bir haritaya yazmaya çalıştığı dosyayı ve satırı da gördüğü yerde bir İstisna atılır.

Ancak, Mapiçerdiği nesnelerin değil , yalnızca kendisinin değişmez olacağını unutmayın .


10
"Hiçbir geliştirici test edilmemiş kodunu yayınlamaz." Bunun üzerine bahis oynamak ister misin? Bu cümle doğru olsaydı, halka açık yazılımlarda çok daha az (varsayımım) hata olurdu. "Çoğu geliştiricinin ..." doğru olacağından bile emin değilim. Ve evet, bu hayal kırıklığı yaratıyor.
Tom

1
Tamam, Tom haklı, ne söylemeye çalıştığından emin değilim, ama yazdıkların açıkça yanlış. (Ayrıca: cinsiyet kapsayıcılığı için
dürtü

@Tom Sanırım bu cevaptaki alayları kaçırdınız
Alma Alma

10

Bunu böyle bırakırsam, diğer geliştiriciler bu nesnenin değişmez olduğu gerçeğini gözden kaçırabilir

Bu doğru, ancak diğer geliştiriciler kodlarını test etmeli ve kapsandığından emin olmalıdır.

Yine de bunu çözmek için 2 seçeneğiniz daha var:

  • Javadoc kullanın

    @return a immutable map
    
  • Açıklayıcı bir yöntem adı seçin

    public Map<String, Integer> getImmutableMap()
    public Map<String, Integer> getUnmodifiableEntries()
    

    Somut bir kullanım örneği için, yöntemleri daha iyi adlandırabilirsiniz. Örneğin

    public Map<String, Integer> getUnmodifiableCountByWords()
    

Başka ne yapabilirim?!

Bir iade edebilirsiniz

  • kopya

    private Map<String, Integer> myMap;
    
    public Map<String, Integer> foo() {
      return new HashMap<String, Integer>(myMap);
    }
    

    Bu yaklaşım, çok sayıda istemcinin haritayı değiştirmesini bekliyorsanız ve harita yalnızca birkaç girdi içerdiği sürece kullanılmalıdır.

  • CopyOnWriteMap

    yazma koleksiyonlarına kopyala genellikle
    eşzamanlılıkla uğraşmanız gerektiğinde kullanılır. Ancak, bir CopyOnWriteMap değişken bir işlemde dahili veri yapısının bir kopyasını oluşturduğundan (örneğin ekle, kaldır) bu konsept size de yardımcı olacaktır.

    Bu durumda, haritanızın etrafında, mutatif işlemler dışında, tüm yöntem çağrılarını temeldeki haritaya delege eden ince bir sarmalayıcıya ihtiyacınız vardır. Değişken bir işlem başlatılırsa, temel alınan haritanın bir kopyasını oluşturur ve diğer tüm çağrılar bu kopyaya devredilecektir.

    Bazı istemcilerin haritayı değiştirmesini bekliyorsanız bu yaklaşım kullanılmalıdır.

    Ne yazık ki java'da böyle bir CopyOnWriteMap. Ancak üçüncü bir taraf bulabilir veya kendiniz uygulayabilirsiniz.

Sonunda haritanızdaki öğelerin hala değiştirilebilir olabileceğini aklınızda bulundurmalısınız.


8

Kesinlikle bir ImmutableMap döndürün, gerekçelendirme:

  • Yöntem imzası (dönüş türü dahil) kendi kendini belgeleyen olmalıdır. Yorumlar müşteri hizmetleri gibidir: Müşterilerinizin onlara güvenmesi gerekiyorsa, birincil ürününüz kusurludur.
  • Bir şeyin bir arabirim mi yoksa bir sınıf mı olduğu, yalnızca onu genişletirken veya uygularken önemlidir. Bir örnek (nesne) verildiğinde, zaman istemci kodunun% 99'u bir şeyin arabirim mi yoksa sınıf mı olduğunu bilmeyecek veya umursamayacaktır. İlk başta ImmutableMap'in bir arayüz olduğunu varsaydım. Ancak bağlantıya tıkladıktan sonra bunun bir sınıf olduğunu anladım.

5

Sınıfın kendisine bağlıdır. Guava'nın ImmutableMap, değişken bir sınıfa yönelik değişmez bir bakış açısı olması amaçlanmamıştır. Sınıfınız değişmez ise ve temelde bir yapıya sahipse ImmutableMap, dönüş türünü yapın ImmutableMap. Ancak, sınıfınız değiştirilebilirse, yapma. Eğer buna sahipseniz:

public ImmutableMap<String, Integer> foo() {
    return ImmutableMap.copyOf(internalMap);
}

Guava haritayı her seferinde kopyalayacaktır. Bu yavaş. Ama internalMapzaten bir ise ImmutableMap, o zaman tamamen sorun değil.

Sınıfınızı geri ImmutableMapdönüşle sınırlamazsanız, bunun yerine şu şekilde geri dönebilirsiniz Collections.unmodifiableMap:

public Map<String, Integer> foo() {
    return Collections.unmodifiableMap(internalMap);
}

Bunun haritanın değişmez bir görünümü olduğunu unutmayın . Eğer internalMapdeğişikliklerin, bu nedenle bir kopyasını önbelleğe alınmış olacaktır Collections.unmodifiableMap(internalMap). Yine de alıcılar için tercih ederim.


1
Bunlar iyi noktalar, ancak bütünlük açısından , sadece referans sahibi tarafından değiştirilemeyeceğini açıklayan bu cevabı beğendim unmodifiableMap- bu bir görünümdür ve arka haritanın sahibi, arka haritayı değiştirebilir ve ardından görünüm değişir. Yani bu önemli bir husus.
davidbak

@ Davidback'in yorumladığı gibi, kullanım konusunda çok dikkatli olun Collections.unmodifiableMap. Bu yöntem, ayrı bir yeni harita nesnesi oluşturmak yerine orijinal Haritaya bir görünüm döndürür . Böylece, orijinal Haritaya atıfta bulunan başka herhangi bir kod onu değiştirebilir ve sonra, sözde değiştirilemez haritanın sahibi, haritanın gerçekten de altından değiştirilebileceğini zor yoldan öğrenir. Ben de bu gerçek tarafından ısırıldım. Ya Haritanın bir kopyasını iade etmeyi ya da Guava'yı kullanmayı öğrendim ImmutableMap.
Basil Bourque

5

Bu, soruyu tam olarak yanıtlamıyor, ancak yine de bir haritanın iade edilip edilmeyeceğini düşünmeye değer. Harita değişmez ise, sağlanacak birincil yöntem get (key) 'e dayanır:

public Integer fooOf(String key) {
    return map.get(key);
}

Bu, API'yi çok daha sıkı hale getirir. Bir harita gerçekten gerekliyse, bu, bir giriş akışı sağlayarak API istemcisine bırakılabilir:

public Stream<Map.Entry<String, Integer>> foos() {
    map.entrySet().stream()
}

Daha sonra müşteri, ihtiyaç duyduğu şekilde kendi sabit veya değiştirilebilir haritasını oluşturabilir veya girişleri kendi haritasına ekleyebilir. Müşterinin değerin var olup olmadığını bilmesi gerekiyorsa, bunun yerine isteğe bağlı olarak döndürülebilir:

public Optional<Integer> fooOf(String key) {
    return Optional.ofNullable(map.get(key));
}

-1

Değişmez Harita bir Harita türüdür. Dolayısıyla, Harita'nın dönüş türünden ayrılmak sorun değil.

Kullanıcıların döndürülen nesneyi değiştirmemesini sağlamak için yöntemin belgeleri, döndürülen nesnenin özelliklerini açıklayabilir.


-1

Bu tartışmalı bir fikir meselesidir, ancak burada daha iyi fikir, harita sınıfı için bir arayüz kullanmaktır. Bu arayüzün açıkça değişmez olduğunu söylemesi gerekmez, ancak arayüzdeki üst sınıfın ayarlayıcı yöntemlerinden herhangi birini açığa çıkarmazsanız mesaj aynı olacaktır.

Aşağıdaki makaleye bir göz atın:

andy gibson


1
Gereksiz sarmalayıcılar zararlı kabul edildi.
djechlin

Haklısın, arayüz yeterli olduğu için burada da bir sarmalayıcı kullanmam.
WSimpson

Bu yapılacak doğru şey olsaydı, ImmutableMap zaten bir ImmutableMapInterface uygulayacaktı, ama bunu teknik olarak anlamıyorum.
djechlin

Mesele Guava geliştiricilerinin amaçladığı değil, bu geliştiricinin mimariyi özetlemek istediği ve ImmutableMap'in kullanımını açığa çıkarmadığı gerçeğidir. Bunu yapmanın bir yolu, bir arayüz kullanmak olacaktır. Belirttiğiniz gibi bir sargı kullanmak gerekli değildir ve bu nedenle tavsiye edilmez. Geri Dönen Harita yanıltıcı olduğu için istenmeyen bir durumdur. Öyle görünüyor ki amaç kapsülleme ise, o zaman bir Arayüz en iyi seçenektir.
WSimpson

MapArayüzü genişletmeyen (veya uygulamayan) bir şeyi döndürmenin dezavantajı, onu herhangi bir API işlevine aktarmadan geçirememenizdir Map. Bu, diğer harita türlerinin her türlü kopya-yapıcılarını içerir. Buna ek olarak, eğer değişkenlik arayüz tarafından yalnızca "gizli" ise, kullanıcılarınız kodunuzu bu şekilde yayınlayarak ve kırarak her zaman bu sorunu çözebilir.
Hulk
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.