Arayüz tasarımında jenerikler ne zaman kullanılır?


11

Üçüncü tarafların gelecekte uygulamaya koymayı düşündüğüm bazı arayüzlerim var ve kendime temel bir uygulama sağlıyorum. Örneği göstermek için sadece bir çift kullanacağım.

Şu anda, bunlar

öğe:

public interface Item {

    String getId();

    String getName();
}

ItemStack:

public interface ItemStackFactory {

    ItemStack createItemStack(Item item, int quantity);
}

ItemStackContainer:

public interface ItemStackContainer {

    default void add(ItemStack stack) {
        add(stack, 1);
    }

    void add(ItemStack stack, int quantity);
}

Şimdi, Itemve ItemStackFactoryben kesinlikle gelecekte uzatmak gerek bazı üçüncü parti öngörebiliriz. ItemStackContainergelecekte de uygulanabilir, ancak öngördüğüm şekillerde değil, sağlanan varsayılan uygulamamın dışında.

Şimdi bu kütüphaneyi mümkün olduğunca sağlam yapmaya çalışıyorum; bu hala erken aşamalarda (alfa öncesi öncesi) olduğundan, bu bir aşırı mühendislik eylemi (YAGNI) olabilir. Burası jenerik ilaçların kullanımı için uygun bir yer mi?

public interface ItemStack<T extends Item> {

    T getItem();

    int getQuantity();
}

Ve

public interface ItemStackFactory<T extends ItemStack<I extends Item>> {

    T createItemStack(I item, int quantity);
}

Bunun uygulama ve kullanımı okumayı ve anlamayı zorlaştıracağından korkuyorum; Bence mümkün olan her yerde jenerik ilaçları yerleştirmekten kaçınmak için bir tavsiye.


1
Uygulama detaylarını her iki şekilde de sergilediğinizi düşünüyoruz. Arayanın neden basit miktarlardan ziyade yığınlarla çalışması ve bilmesi / bakımı gerekiyor?
cHao

Bu hiç düşünmediğim bir şeydi. Bu, bu özel sorun hakkında hoş bir fikir vermektedir. Kodumda değişiklik yaptığınızdan emin olacağım.
Zymus

Yanıtlar:


9

Uygulamanız da genel olacağı zaman, arayüzünüzde jenerikler kullanırsınız.

Örneğin, keyfi nesneleri kabul edebilen herhangi bir veri yapısı, genel bir arayüz için iyi bir adaydır. Örnekler: List<T>ve Dictionary<K,V>.

Tip güvenliğini genel bir şekilde geliştirmek istediğiniz herhangi bir durum jenerikler için iyi bir adaydır. A List<String>, yalnızca dizelerde çalışan bir listedir.

SRP'yi genelleştirilmiş bir şekilde uygulamak ve türe özgü yöntemlerden kaçınmak istediğiniz herhangi bir durum, jenerikler için iyi bir adaydır. Örneğin, bir PersonDocument, PlaceDocument ve ThingDocument bir ile değiştirilebilir Document<T>.

Soyut Fabrika modeli genel için iyi bir kullanım örneğidir, sıradan bir Fabrika Yöntemi ise ortak, somut bir arayüzden miras kalan nesneleri yaratacaktır.


Jenerik arayüzlerin jenerik uygulamalarla eşleştirilmesi gerektiği fikrine katılmıyorum. Bir arabirimde genel bir tür parametresinin bildirilmesi ve daha sonra çeşitli uygulamalarda düzeltilmesi veya somutlaştırılması güçlü bir tekniktir. Özellikle Scala'da yaygındır. Soyut şeyleri birbirine bağlar; sınıflar uzmanlaşır.
Benjamin Hodgson

@BenjaminHodgson: Bu, yalnızca belirli türler için genel bir arayüzde uygulamalar ürettiğiniz anlamına gelir. Biraz daha az olsa da, genel yaklaşım hala geneldir.
Robert Harvey

Katılıyorum. Belki de cevabınızı yanlış anladım - böyle bir tasarıma karşı tavsiyede bulunuyordunuz.
Benjamin Hodgson

@BenjaminHodgson: Bir Fabrika Yöntemi , genel bir yöntem yerine somut bir arabirime karşı yazılmaz mı? (bir Soyut Fabrika genel olsa da)
Robert Harvey

Sanırım kabul ediyoruz :) Muhtemelen OP'nin örneğini benzer bir şey olarak yazardım class StackFactory { Stack<T> Create<T>(T item, int count); }. Daha fazla açıklama istiyorsanız, bir cevaba "alt sınıftaki bir tür parametresini düzelterek" ne demek istediğimin bir örneğini yazabilirim, ancak cevap sadece orijinal soru ile teğet olarak ilgili olacaktır.
Benjamin Hodgson

8

Arayüzünüze genelliği nasıl tanıtacağınıza dair planınız benim için doğru görünüyor. Ancak bunun iyi bir fikir olup olmayacağı sorusu biraz daha karmaşık bir cevap gerektirir.

Yıllar boyunca, çalıştığım şirketler adına Yazılım Mühendisliği pozisyonları için birkaç adayla röportaj yaptım ve iş arayanların çoğunluğunun genel sınıfları kullanmaktan rahat olduğunu fark ettim (örneğin, standart koleksiyon sınıfları), ancak genel sınıflar yazarken değil . Birçoğu kariyerinde tek bir jenerik sınıf yazmadı ve bir tane yazması istendiğinde tamamen kaybedilecek gibi görünüyor. Dolayısıyla, jeneriklerin kullanımını arayüzünüzü uygulamak veya temel uygulamalarınızı genişletmek isteyen herhangi birine zorlarsanız, insanları erteliyor olabilirsiniz.

Bununla birlikte, deneyebileceğiniz şey, arabirimlerinizin hem jenerik hem de jenerik olmayan versiyonlarını ve temel uygulamalarınızı sağlamaktır, böylece jenerik yapmayan insanlar dar, tür dökme-ağır dünyalarında mutlu bir şekilde yaşayabilirler. jenerikler dili daha geniş anlamalarının faydalarından yararlanabilir mi?


3

Şimdi, Itemve ItemStackFactoryben kesinlikle gelecekte uzatmak gerek bazı üçüncü parti öngörebiliriz.

Sizin için verdiğiniz karar. Jenerik sağlamazsanız, Itemher kullandıklarında uygulamalarına uymaları gerekir:

class MyItem implements Item {
}
MyItem item = new MyItem();
ItemStack is = createItemStack(item, 10);
// They would have to cast here.
MyItem theItem = (MyItem)is.getItem();

En azından ItemStackönerdiğiniz şekilde jenerik yapmalısınız. Jeneriklerin en büyük yararı, neredeyse hiçbir zaman bir şey yapmanıza gerek olmamasıdır ve kullanıcıları yayınlamaya zorlayan kod sunmak IMHO'yu ceza gerektiren bir suçtur.


2

Vay be ... Buna tamamen farklı bir bakış açım var.

Bazı üçüncü tarafların ihtiyacını kesinlikle öngörebiliyorum

Bu bir hata. "Gelecek için" kodu yazmamalısınız.

Ayrıca, arayüzler yukarıdan aşağıya yazılır. Bir sınıfa bakmaz ve "Hey, bu sınıf tamamen bir arayüz uygulayabilir ve sonra o arayüzü başka bir yerde de kullanabilirim" demezsiniz. Bu yanlış bir yaklaşımdır, çünkü yalnızca bir arabirimi kullanan sınıf arabirimin ne olması gerektiğini gerçekten bilir. Bunun yerine "Bu noktada sınıfımın bir bağımlılığa ihtiyacı var. Bu bağımlılık için bir arabirim tanımlayayım. Bu sınıfı yazmayı bitirdiğimde, bu bağımlılığın bir uygulamasını yazacağım." Birinin arayüzünüzü tekrar kullanıp kullanamayacağı ve varsayılan uygulamanızı kendi ile değiştirip değiştirmeyeceği tamamen önemsizdir. Asla yapamayabilirler. Bu, arayüzün yararsız olduğu anlamına gelmez. Kodunuzu birçok yerde kodunuzu çok genişletilebilir kılan Kontrol Ters Çevirme ilkesini izler "


0

Durumunuzda jenerik ilaç kullanmanın çok karmaşık olduğunu düşünüyorum.

Arayüzlerin jenerik versiyonunu seçerseniz, tasarım

  1. ItemStackContainer <A> tek bir yığın türü içerir, A (yani ItemStackContainer birden çok yığın türü içeremez.)
  2. ItemStack belirli bir öğe türüne ayrılmıştır. Yani her türlü yığın için soyutlama türü yoktur.
  3. Her bir öğe türü için belirli bir ItemStackFactory tanımlamanız gerekir. Fabrika Deseni olarak çok iyi kabul edilir.
  4. ItemStack <? Gibi daha zor kodlar yazın? Kalıcılığı azaltarak Öğe> 'yi genişletir.

Bu örtülü varsayımlar ve kısıtlamalar dikkatle karar vermek için çok önemlidir. Bu nedenle, özellikle erken aşamada, bu erken tasarımlar tanıtılmamalıdır. Basit, anlaşılması kolay, ortak tasarım istenir.


0

Ben esas olarak bu soruya bağlıdır söyleyebilirim: Biri işlevselliğini genişletmek gerekiyorsa Itemve ItemStackFactory,

  • yöntemlerin üzerine yazacaklar mı ve böylece alt sınıflarının örnekleri de temel sınıflar gibi kullanılacak, sadece farklı davranacaklar mı?
  • yoksa üye ekleyip alt sınıfları farklı mı kullanacaklar ?

İlk durumda, jeneriklere gerek yoktur, ikincisinde muhtemelen vardır.


0

Belki, Foo'nun bir arayüz, Bar'ın başka bir arayüz olduğu bir arayüz hiyerarşisine sahip olabilirsiniz. Bir türün her ikisi de olması gerektiğinde, bu bir FooAndBar'dır.

Aşırı tasarımdan kaçınmak için, sadece gerektiğinde FooAndBar türünü yapın ve hiçbir şeyi genişletmeyecek arabirimlerin bir listesini tutun.

Bu, çoğu programcı için çok daha rahattır (çoğu tür denetimleri gibi veya herhangi bir tür statik yazımla asla uğraşmak istemez). Çok az insan kendi jenerik sınıflarını yazdı.

Ayrıca not: arayüzler hangi olası eylemlerin gerçekleştirilebileceğiyle ilgilidir. Aslında isimler değil fiiller olmalılar.


Bu eşdeğer ilaç kullanımıyla alternatif olabilir, ama asıl soru sorulduğunda ne zaman sen ne önermek üzerinde jeneriği kullanmak. Jeneriklerin hiç gerekli olmadığını önermek için cevabınızı geliştirebilir misiniz, ancak birden fazla arayüz kullanmak herkesin cevabı mı?
Jay Elston
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.