Arayüzlerin gelecekteki kullanımı için programlama


42

Böyle bir arayüz tasarlayan yanımda oturan bir meslektaşım var:

public interface IEventGetter {

    public List<FooType> getFooList(String fooName, Date start, Date end)
        throws Exception;
    ....

}

Sorun şu ki, kodumuzda bu "end" parametresini kullanmıyoruz, tam orada, çünkü gelecekte biraz zaman kullanmak zorunda kalabiliriz.

Şu anda kullanımda olmayan arayüzlere parametreler koymanın kötü bir fikir olduğuna ikna etmeye çalışıyoruz, ancak bir süre "bitiş" tarihini uygularsak, çok fazla iş yapılması gerektiği konusunda ısrar ediyor. sonra ve sonra tüm kodu uyarlamanız gerekir.

Şimdi, benim sorum şu, onunla bağlantı kurabileceğimiz "saygın" kodlama gurularının böyle bir konusunu ele alan herhangi bir kaynak var mı?


29
"Tahmin, özellikle gelecekle ilgili çok zor."
Jörg W Mittag

10
Sorunun bir kısmı, başlamak için en iyi arayüz olmamasıdır. Foos'u almak ve filtrelemek iki ayrı konu. Bu arayüz sizi listeyi belirli bir şekilde filtrelemeye zorluyor; çok daha genel bir yaklaşım, foo'nun dahil edilip edilmemesi gerektiğine karar veren bir fonksiyona geçmek. Ancak, bu yalnızca Java 8’e erişiminiz varsa çok zarif.
Doval

5
Sayaç noktasıdır. Basitçe şu an için yalnızca ihtiyacınız olanları içeren ve sonunda endo nesneye parametre ekleyebilecek ve hatta kodu
kırmamak

3
Java 8 varsayılan yöntemleri ile gereksiz argümanlar eklemek için hiçbir sebep yoktur. Daha sonra tanımlandığı gibi basit çağıran varsayılan bir uygulamayla bir yöntem eklenebilir null. Uygulama sınıfları daha sonra gerektiğinde geçersiz kılınabilir.
Örümcek Boris,

1
@Doval Java hakkında bir şey bilmiyorum, ancak .NET'te bazen IQueryableDAL
Ben Aaronson

Yanıtlar:


62

Onu YAGNI hakkında bilgi almaya davet et . Wikipedia sayfasının gerekçesi burada özellikle ilginç olabilir:

YAGNI yaklaşımını savunanlara göre, şu anda gerekli olmayan ancak gelecekte olabilecek kod yazma cazibesi aşağıdaki dezavantajlara sahiptir:

  • Gerekli işlevselliğin eklenmesi, test edilmesi veya iyileştirilmesi için harcanan zaman alınır.
  • Yeni özellikler hata ayıklanmalı, belgelendirilmeli ve desteklenmelidir.
  • Herhangi bir yeni özellik gelecekte neler yapılabileceği konusunda kısıtlamalar getirir, bu nedenle gereksiz bir özellik, gerekli özelliklerin gelecekte eklenmesini engelleyebilir.
  • Özelliğe ihtiyaç duyulana kadar ne yapması gerektiğini tam olarak tanımlamak ve test etmek zor. Yeni özellik doğru bir şekilde tanımlanmamış ve test edilmemişse, nihayetinde gerekli olsa bile doğru çalışmayabilir.
  • Kod şişirilmesine yol açar; yazılım daha büyük ve daha karmaşık hale gelir.
  • Spesifikasyonlar ve bir çeşit revizyon kontrolü olmadığı sürece, bu özelliği kullanabilecek programcılar tarafından bilinmeyebilir.
  • Yeni özellik eklemek, başka yeni özellikler önerebilir. Bu yeni özellikler de uygulanırsa, bu özellik sürünme üzerinde kartopu etkisine neden olabilir.

Diğer olası argümanlar:

  • “Bir yazılımın kullanım ömrünün% 80'i bakım işlemine gidiyor” . Tam zamanında kod yazmak bakımın maliyetini düşürür: bir kişi daha az kod bulundurmak zorundadır ve gerçekten gereken koda odaklanabilir.

  • Kaynak kodu bir kez yazılır, ancak onlarca kez okuyun. Herhangi bir yerde kullanılmayan ek bir argüman, neden gerekli olmayan bir argüman olduğunu anlamayı boşa harcar. Bunun birkaç olası uygulamaya sahip bir arayüz olduğu düşünüldüğünde işleri daha da zorlaştırır.

  • Kaynak kodun kendi kendini belgelemesi bekleniyor. Gerçek imza yanıltıcıdır, çünkü bir okuyucu bunun endsonucunu veya yöntemin uygulanmasını etkileyeceğini düşünecektir .

  • Bu arabirimin somut uygulamalarını yazan kişiler, son argümanın kullanılmaması gerektiğini, bu da farklı yaklaşımlara yol açacağını anlayamayabilir:

    1. İhtiyacım yok end, o yüzden değerini görmezden geleceğim.

    2. İhtiyacım yok end, öyleyse bir istisna atacağım null,

    3. İhtiyacım yok endama bir şekilde kullanmaya çalışacağım.

    4. endİhtiyaç duyduğunuzda daha sonra kullanılabilecek çok sayıda kod yazacağım .

Ancak meslektaşınızın haklı olabileceğini unutmayın.

Önceki tüm noktalar yeniden yapılanmanın kolay olduğu gerçeğine dayanmaktadır, bu nedenle daha sonra bir argüman eklemek fazla çaba gerektirmez. Ancak bu bir arayüzdür ve bir arayüz olarak, ürününüzün diğer bölümlerine katkıda bulunan birkaç ekip tarafından kullanılabilir. Bu, bir arayüz değiştirmenin özellikle acı verici olabileceği anlamına gelir; bu durumda, YAGNI burada gerçekten geçerli değildir.

Hjk tarafından verilen cevap iyi bir çözüm sunar: önceden kullanılmış bir arayüze bir yöntem eklemek çok zor değildir, ancak bazı durumlarda da önemli bir maliyeti vardır:

  • Bazı çerçeveler aşırı yüklemeyi desteklemiyor. Örneğin ve iyi hatırlıyorsam (yanılıyorsam düzeltin), .NET'in WCF'si aşırı yüklenmeleri desteklemiyor.

  • Arabirimin birçok somut uygulaması varsa, ara yüze bir yöntem eklemek tüm uygulamaların üzerinden geçmeyi ve oraya yöntemi eklemeyi gerektirir.


4
Gelecekte bu özelliğin ortaya çıkma ihtimalini göz önünde bulundurmanın önemli olduğunu düşünüyorum. Kesin olarak biliyorsanız, eklenecek ancak şu anda herhangi bir nedenden ötürü değil. O zaman bunun sadece bir "durum" özelliği olmadığı için akılda tutulması gereken bir argüman olduğunu söyleyebilirim.
Jeroen Vannevel

3
@JeroenVannevel: kesinlikle, ama bu çok spekülatif. Tecrübelerime göre, kesinlikle uygulanacağına inandığım özelliklerin çoğu iptal edildi veya sonsuza dek ertelendi. Bu, başlangıçta paydaşlar tarafından çok önemli olarak vurgulanan özellikleri içerir.
Arseni Mourzenko

26

ancak bir süre sonra "bitiş" tarihini uygularsak ve tüm kodu o zaman uyarlamak zorunda kalırsak, çok fazla iş yapılması gerektiği konusunda ısrar ediyor.

(Bir süre sonra)

public class EventGetter implements IEventGetter {

    private static final Date IMPLIED_END_DATE_ASOF_20140711 = new Date(Long.MAX_VALUE); // ???

    @Override
    public List<FooType> getFooList(String fooName, Date start) throws Exception {
        return getFooList(fooName, start, IMPLIED_END_DATE_ASOF_20140711);
    }

    @Override
    public List<FooType> getFooList(String fooName, Date start, Date end) throws Exception {
        // Final implementation goes here
    }
}

Tek ihtiyacın olan şey, yöntem aşırı. Gelecekteki ek yöntem, mevcut yönteme yapılan çağrıları etkilemeden şeffaf bir şekilde tanıtılabilir.


6
Sizin değişikliğiniz artık onun bir arayüz olmadığı ve bir nesnenin zaten bir nesneden kaynaklandığı ve arayüzü uyguladığı durumlarda kullanılması mümkün olmayacağı dışında. Arabirimi uygulayan tüm sınıflar aynı projedeyse (takip derleme hataları) önemsiz bir sorundur. Birden fazla proje tarafından kullanılan bir kütüphane ise, alanın eklenmesi, önümüzdeki x aylar / yıllar boyunca sporadik zamanlarda kafa karışıklığına ve diğer insanlara yönelik çalışmaya neden olur.
Kieveli

1
Eğer OP ekibi (iş arkadaşını kurtar), endparametrenin şu anda gereksiz olduğuna ve endproje boyunca -less yöntemini kullanmaya devam ettiğine inanıyorsa , arayüzü güncellemek endişelerinin en küçüğüdür, çünkü güncellemeyi zorunluluk haline getirir. uygulama sınıfları Cevabım özellikle "tüm kodu adapte et" kısmını hedefliyor, çünkü meslektaşım üçüncü bir varsayılan / kukla parametresini tanıtmak için proje (ler) boyunca orijinal yöntemin arayanlarını güncellemek zorunda kalıyor gibi görünüyordu. .
hjk

18

[Onu ikna etmek için] bağlayabileceğimiz "saygın" kodlama guruları var mı?

Otoriteye yapılan itirazlar özellikle ikna edici değildir; Kim olursa olsun geçerli bir argüman sunmak daha iyi.

İşte , iş arkadaşınızın duyarlılığından bağımsız olarak "haklı" olmak zorunda kaldığına ikna etmesi ya da göstermesi gereken bir indirim ve eksiklik:


Gerçekten ihtiyacın olan şey

getFooList(String fooName, Date start, Date end, Date middle, 
           Date one_third, JulianDate start, JulianDate end,
           KlingonDate start, KlingonDate end)

Klingon için ne zaman uluslararasılacağınızı asla bilemezsiniz, bu nedenle şimdi halletmeniz daha iyi olacaktır çünkü güçlendirme için çok fazla çalışma gerektirecektir ve Klingons'un sabrı ile bilinmemektedir.


22
You never know when you'll have to internationalize for Klingon. Bu yüzden a'yı geçmelisiniz Calendarve müşteri kodunun bir GregorianCalendarveya bir tane göndermek isteyip istemediğine karar vermesine izin vermelisiniz KlingonCalendar. Duh. :-p
SJuan76

3
Peki ya StarDate sdStartve StarDate sdEnd? Sonuçta Federasyon hakkında unutamam ...
WernerCD

Bütün bunlar açıkça alt sınıflarına dönüştürülebilir ya da buna dönüştürülebilir Date!
Darkhogg

Keşke bu kadar basit olsaydı .
msw

@ msw, "saygı duyulan kodlama guruları" ile bağlantı kurmasını istediğinde "otoriteye itiraz" istediğinden emin değilim. Söylediğin gibi, "kim söyledi olursa olsun geçerli bir argümana" ihtiyacı var. "Guru" tipi insanlar bunların çoğunu bilme eğilimindedir. Ayrıca unuttun HebrewDate startve HebrewDate end.
trysis

12

Bir yazılım mühendisliği bakış açısına göre, bu tür sorunlara uygun çözümün üretici modelinde olduğuna inanıyorum . Bu kesinlikle meslektaşınızın http://en.wikipedia.org/wiki/Builder_pattern için 'guru' yazarlarından bir bağlantıdır .

Oluşturucu düzeninde, kullanıcı parametreleri içeren bir nesne oluşturur. Bu parametre kabı daha sonra yönteme geçirilecektir. Bu, meslektaşınızın ileride ihtiyaç duyacağı parametrelerin uzatılması ve aşırı yüklenilmesi ile ilgilenirken, değişiklik yapılması gerektiğinde her şeyi çok kararlı hale getirecektir.

Örneğin olacak:

public interface IEventGetter {
    public List<FooType> getFooList(ISearchFooRequest req) {
        throws Exception;
    ....
    }
}

public interface ISearchFooRequest {
        public String getName();
        public Date getStart();
        public Date getEnd();
        public int getOffset();
        ...
    }
}

public class SearchFooRequest implements ISearchFooRequest {

    public static SearchFooRequest buildDefaultRequest(String name, Date start) {
        ...
    }

    public String getName() {...}
    public Date getStart() {...}
    ...
    public void setEnd(Date end) {...}
    public void setOffset(int offset) {...}
    ...
}

3
Bu iyi bir genel yaklaşım, ama bu durumda sadece gelen sorunu itmek gibi görünüyor IEventGetteriçin ISearchFootRequest. Bunun yerine , öğeyi ekleyip eklememeyi döndüren public IFooFilter, üyeye benzer bir şey öneririm public bool include(FooType item). O zaman bireysel arayüz uygulamaları bu filtrelemeyi nasıl yapacaklarına karar verebilir
Ben Aaronson

@Ben Aaronson Sadece Request parametre nesnesinin nasıl oluşturulduğunu göstermek için kodu düzenledim
InformedA

Oluşturucu burada gereksizdir (ve açıkçası, aşırı kullanılmış / aşırı önerilen bir model); Parametrelerin nasıl ayarlanması gerektiğine dair karmaşık kurallar yoktur, bu nedenle karmaşık veya sık sık değiştirilen metod parametrelerine ilişkin meşru bir kaygı varsa, bir parametre nesnesi tamamen iyidir. Ve BenBenAaronson ile aynı fikirdeyim, ancak bir parametre nesnesi kullanmanın amacı yarasadaki gereksiz parametreleri dahil etmemek , ancak daha sonra eklemelerini çok kolaylaştırmak (çoklu arayüz uygulamaları olsa bile).
Aarona

7

Şimdi ihtiyacınız yok, o yüzden eklemeyin. Daha sonra ihtiyacınız olursa arayüzü genişletin:

public interface IEventGetter {

    public List<FooType> getFooList(String fooName, Date start)
         throws Exception;
    ....

}

public interface IBoundedEventGetter extends IEventGetter {

    public List<FooType> getFooList(String fooName, Date start, Date end)
        throws Exception;
    ....

}

Mevcut (ve muhtemelen zaten test edilmiş / sevk edilmiş) arayüzünü değiştirmemek için +1.
Sebastian Godelet

4

Hiçbir tasarım ilkesi mutlak değildir, bu yüzden diğer cevaplara daha çok katılıyorum, ancak şeytanın avukatı oynayacağımı ve meslektaşınızın çözümünü kabul etmeyi düşündüğüm bazı koşulları tartışacağımı düşündüm:

  • Bu bir genel API ise ve bu özelliği dahili olarak kullanmasanız bile, üçüncü taraf geliştiriciler için yararlı olacağını umuyorsunuz.
  • Sadece gelecekteki faydaları değil, önemli acil faydaları varsa. Örtülü bitiş tarihi ise Now(), bir parametre eklemek, önbellekleme ve ünite testi için yararları olan bir yan etkiyi ortadan kaldırır. Belki daha basit bir uygulamaya izin verir. Veya belki de kodunuzdaki diğer API'ler ile daha tutarlı.
  • Gelişim kültürünüzün sorunlu bir geçmişi varsa. Süreçler veya bölgesellik, bir arayüz gibi merkezi bir şeyi değiştirmeyi çok zorlaştırıyorsa, daha önce gördüğüm şey, arayüzü değiştirmek yerine müşteri tarafı geçici çözümler uygulayan insanlar, o zaman bir düzine bitiş tarihi sürdürmeye çalışıyorsunuz bir yerine filtreler. Bu tür bir şey şirketinizde çok olursa, gelecekteki provalara biraz daha çaba sarf etmeniz mantıklı olacaktır. Beni yanlış anlamayın, gelişim süreçlerinizi değiştirmek daha iyidir , ancak bu genellikle söylenenden daha kolaydır.

Olduğu söyleniyor, benim deneyimimde YAGNI'nin en büyük sonucu YDKWFYN: Hangi Neye İhtiyacınız Olduğunu Bilmiyorsunuz (evet, sadece bu kısaltmayı hazırladım). Bir limit parametresine ihtiyaç duyulsa bile nispeten öngörülebilir olsa bile , bir sayfa sınırı, gün sayısı veya bir kullanıcı tercihleri ​​tablosundan veya herhangi bir şeyden bitiş tarihini kullanmayı belirten bir boolean biçimini alabilir.

Henüz bir gereksiniminiz olmadığından, bu parametrenin ne tür olması gerektiğini bilmenin hiçbir yolu yoktur. Sıklıkla ya sizin ihtiyacınıza en uygun olmayan garip bir arayüze sahip olursunuz ya da yine de değiştirmek zorunda kalırsınız.


2

Bu soruyu cevaplamak için yeterli bilgi yok. Gerçekten ne yaptığına getFooListve nasıl yapacağına bağlı.

Burada kullanılan veya kullanılmayan ilave bir parametreyi desteklemesi gereken açık bir yöntem örneği.

void CapitalizeSubstring (String capitalize_me, int start_index);

Koleksiyonun başlangıcını belirleyebileceğiniz ancak koleksiyonun sonunu belirleyemediğiniz bir koleksiyonda çalışan bir yöntem uygulamak genellikle saçmadır.

Sorunun kendisine gerçekten bakmanız ve parametrenin tüm arabirim bağlamında saçma olup olmadığını ve ek parametrenin ne kadar yük getirdiğini sormanız gerekir.


1

Korkarım meslektaşın gerçekten geçerli bir noktaya sahip olabilir. Çözümü aslında en iyi olmasa da.

Önerilen arayüzünden anlaşıldığı kadarıyla

public List<FooType> getFooList(String fooName, Date start, Date end) throws Exception;

zaman içinde bulunan örnekleri döndürüyor. İstemciler şu anda end parametresini kullanmıyorsa, bu, zaman içinde bulunan örnekleri bekledikleri gerçeğini değiştirmez. Bu sadece şu anda tüm istemcilerin açık uçlu aralıkları kullandığı anlamına gelir (başlangıçtan sonsuzluğa)

Yani daha iyi bir arayüz olacaktır:

public List<FooType> getFooList(String fooName, Interval interval) throws Exception;

Statik fabrika yöntemiyle aralık sağlıyorsanız:

public static Interval startingAt(Date start) { return new Interval(start, null); }

o zaman müşteriler bir bitiş zamanı belirleme ihtiyacı hissetmezler bile.

Aynı zamanda arayüzünüz ne yaptığını daha doğru bir şekilde iletir, çünkü getFooList(String, Date)bunun bir aralık ile ilgilendiğini bildirmez.

Önerimin, şu anda yöntemin ne yaptığını, gelecekte yapılması gerekenden ya da yapabileceğinden değil ve YAGNI prensibinin (gerçekten çok geçerli olan) burada geçerli olmadığından kaynaklandığına dikkat edin.


0

Kullanılmayan bir parametre eklemek kafa karıştırıcıdır. İnsanlar bu özelliğin işe yarayacağını farz ederek bu yöntemi söyleyebilir.

Eklemezdim. Daha sonra bir yeniden düzenleme kullanarak ve çağrı sitelerini mekanik olarak sabitleyerek daha sonra eklemek çok önemlidir. Statik olarak yazılmış bir dilde bunu yapmak kolaydır. Hevesle eklemek için gerekli değildir.

Bu parametreye sahip olmak için bir neden varsa karışıklığı önleyebilirsiniz: Bu parametrenin varsayılan değer olarak iletilmesini zorlamak için bu arabirimin uygulanmasında bir iddia ekleyin. Birisi yanlışlıkla bu parametreyi kullanıyorsa, test sırasında bu özelliğin uygulanmadığını derhal fark edecektir. Bu, bir böceğin üretime geçme riskini ortadan kaldırır.

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.