Sadece bir sınıfın uygulayacağı bir arayüz kullanmam gerekir mi?


243

Çoklu sınıfların oluşturduğu bir arayüzün amacı bir dizi kural ve uygulamaya uymuyor mu?



16
Veya en içten çekmeyi kolaylaştırmak için.

11
Birden fazla sınıfın Arabirimleri uygulamalarına izin vermek ve kodunuzu arabirimlere bağlı tutmak, ünite testi için yalıtımı zorunludur. Birim testi yapıyorsanız, bu arayüzü uygulayan başka bir sınıfa sahip olacaksınız.
StuperUser

2
Bu soru üzerine tartışmayı reddet .
yannis

6
Genel alanlar ve yöntemler kendi başlarına bir "arayüz" dür. Polimorfizm eksikliği kasıtlı olarak planlanmışsa, bir arayüz kullanmak için hiçbir sebep yoktur. Bahsedilen diğerlerini test eden birim polimorfizmin planlı bir kullanımıdır.
mike30 19

Yanıtlar:


205

Açıkçası, hayır, yapmazsınız , YAGNI uygulanır. Bununla birlikte, arayüzü oluşturmak için harcayacağınız zaman minimumdur, özellikle işin çoğunu sizin için yapan kullanışlı bir kod oluşturma aracınız varsa. Arayüzüne ihtiyacınız olup olmayacağından emin değilseniz, ara yüz tanımını destekleme yönüne doğru yanılmanın daha iyi olacağını söyleyebilirim.

Ayrıca, tek bir sınıf için bile bir arayüz kullanmak, üretimde olmayan, birim testleri için başka bir sahte uygulama sağlayacaktır. Avner Shahar-Kashtan'ın cevabı bu noktada genişliyor.


84
+1 testi, neredeyse her zaman iki uygulamanızın olduğu anlamına gelir
jk.

24
@YannisRizos Yagni yüzünden son noktanıza katılmıyorum. Bir sınıfın ortak yöntemlerinden bir arabirimin kranklanması, CFoo'yu tüketici sınıflarında IFoo ile değiştirmek gibi, önemsizdir. İhtiyacı önceden yazmanın anlamı yok.
Dan Neely,

5
Hala nedenini takip ettiğimden emin değilim. Kod oluşturma araçları onu daha da ucuz hale getirdikten sonra eklemeye başladığından, açık bir ihtiyaç duymadan önce arayüzü oluşturmak için daha az neden görüyorum.
Dan Neely

6
Kayıp bir Arayüzün YAGNI için değil, "kırık bir Pencere" ve eksik Belgeler için iyi bir örnek olduğunu düşünüyorum. Sınıfın kullanıcıları pratikte soyutlamaları yerine uygulamaya karşı kodlamaları zorunludur.
Fabio Fracassi,

10
Neden sadece bazı test çerçevelerini tatmin etmek için kod tabanınızı anlamsız bir sıkıntıyla kirletiyorsunuz? Ciddiyim, ben sadece kendi kendine öğretilen bir JavaScript istemci tarafı çocuğuyum, WTF'yi C # ve Java geliştiricisi OOD uygulamalarında yanlış bulmaya çalışıyorum. IDE'yi ellerinizden tokatlarlar ve üniversitede böyle bir şey yaparken sizi yakaladıklarında temiz ve okunaklı bir kod yazmayı öğrenene kadar geri almanıza izin vermezler mi? Bu sadece müstehcen.
Erik,

144

Sana bir arabirim gerek olup olmadığı anlamına cevap verecek değil birçok sınıfları bunu nasıl uygulayacağınızı bağlıdır. Arabirimler, uygulamanızın birden fazla alt sistemi arasındaki sözleşmeleri tanımlamak için bir araçtır ; Yani asıl önemli olan, başvurunuzun alt sistemlere nasıl bölündüğüdür. Kapsanan altsistemleri ön plana çıkaran, kaç tane sınıf uyguladıklarına bakmadan arayüzler olmalıdır.

İşte çok kullanışlı bir kural:

  • FooDersi doğrudan sınıfa yönlendirirseniz BarImpl, Fooher değiştiğinde kendiniz değişmeye kararlısınız BarImpl. Temel olarak, bunları iki sınıfa bölünmüş bir kod birimi olarak görüyorsunuz.
  • Eğer yaparsanız Foobakın arayüzüne Bar yapmanız yerine kendinizi işliyorsun önlemek için değişiklikler Foodeğiştirmek zaman BarImpl.

Arayüzleri uygulamanızın kilit noktalarında tanımlarsanız, desteklemeleri gereken ve yapmamaları gereken yöntemler hakkında dikkatli düşünürsünüz ve bir uygulamanın nasıl davranması gerektiğini (ve nasıl yapmaması gerektiğini) açık bir şekilde yorumluyorsunuz, Uygulamanızın anlaşılması çok daha kolay olacaktır, çünkü bu yorumlanmış arayüzler uygulamanın bir tür belirtimini sunacaktır - nasıl davranması amaçlandığının bir açıklaması . Bu, kodu okumayı çok daha kolaylaştırır ("bu kodun ne yapması gerektiğini" sormak yerine, bu kodun yapması gerekeni nasıl yaptığını "sormak yerine").

Tüm bunlara ek olarak (veya aslında bundan dolayı), arayüzler ayrı derlemeyi teşvik eder. Arabirimler derleme için önemsiz ve uygulamalarından daha az bağımlılığa sahip olduklarından, Foobir arabirim kullanmak için sınıf Baryazarsanız, genellikle yeniden derlemeye BarImplgerek kalmadan yeniden derleyebilirsiniz Foo. Büyük uygulamalarda bu çok zaman kazandırabilir.


14
Bunu yapabilseydim, bir kereden fazla daha fazla oy kullandım IMO bu sorunun en iyi cevabı.
Fabio Fracassi,

4
Sınıf sadece bir rol oynuyorsa (yani bunun için tanımlanmış bir arayüze sahipse), neden bunu kamu / özel yöntemlerle yapmıyorsunuz?
Matthew Finlay

4
1. Kod organizasyonu; Arayüzün kendi dosyasında sadece imza ve dokümantasyon yorumlarını içeren olması kodun temiz tutulmasına yardımcı olur. 2. Sizi özel tutmak istediğiniz yöntemleri ortaya çıkarmaya zorlayan çerçeveler (örneğin, kamuoyu belirleyiciler aracılığıyla bağımlılıkları enjekte eden kaplar). 3. Daha önce de belirtildiği gibi ayrı derlemeler; sınıf eğer Fooarayüzünde bağlıdır Barsonra BarImplyeniden derlemek zorunda kalmadan değiştirilebilir Foo. 4. Genel / özel tekliflerden daha ince taneli erişim kontrolü (aynı sınıfı, farklı arayüzlerle iki müşteriye gösterin).
sakundim

3
Ve sonuncusu: 5. Müşteriler (ideal olarak) modülümün kaç sınıfa sahip olduğunu veya kaç tane, hatta söyleyebileceği umrunda olmamalıdır. Onlar görmelisiniz Tüm kümesidir türleri ve bazı fabrikalar veya cepheler haftaki almak için. Yani, kütüphanenizin hangi sınıflardan oluştuğunu bile kapsayıcı olarak değer olarak görürüm.
Ağustos'ta 12:01

4
@sacundim BarImpl'yi If you make class Foo refer directly to class BarImpl, you're strongly committing yourself to change Foo every time you change BarImpldeğiştirirken, Foo'da kullanmaktan kaçınılabilecek değişiklikler interface Barnelerdir? Bir yöntemin imzaları ve işlevselliği BarImpl'de değişmediği için Foo, arabirim olmadan da (arabirimin tüm amacı) değişiklik gerektirmez. Sadece bir sınıfın yani BarImplBar'ı uyguladığı senaryodan bahsediyorum . Çok sınıflı senaryo için Bağımlılık İnversiyon Prensibi'ni ve bunun arayüzünün nasıl yardımcı olduğunu biliyorum.
Shishir Gupta

101

Arabirimler, bir davranışı, yani bir işlev / yöntem prototip kümesini tanımlayacak şekilde tasarlanmıştır. Arabirimi uygulayan türler bu davranışı uygular; bu nedenle, böyle bir türle uğraşırken ne tür bir davranış olduğunu biliyorsunuzdur.

Tanımladığı davranışın yalnızca bir kez kullanılacağını biliyorsanız, bir arabirim tanımlamanıza gerek yoktur. KISS (basit tut, aptal)


Her zaman değil, o sınıf genel bir sınıfsa, bir sınıfta arabirimi kullanabilirsiniz.
John Isaiah Carmona

Dahası, modern IDE'de nihayet kolayca gerektiğinde bir arabirim "yeniden düzenleme" ile yeniden düzenleme yapabilirsiniz.
Hans-Peter Störr,

Joshua Bloch ve ark. Gibi yazarlar tarafından tamamen kabul edilebilir ve teşvik edilen, yalnızca kamu statik yöntemlerinin ve özel bir kurucunun bulunduğu bir hizmet sınıfı düşünün.
Darrell Teague

63

Teoride, sadece bir arayüzün iyiliği için bir arayüzün olmamasına rağmen, Yannis Rizos'un cevabı başka komplikasyonlara işaret ediyor:

Birim testleri yazarken ve Moq veya FakeItEasy gibi sahte çerçeveler kullanırken (kullandığım en yeni iki ismi adlandırmak için), dolaylı olarak arayüzü uygulayan başka bir sınıf yaratıyorsunuzdur. Kod veya statik analizlerde arama yapmak, yalnızca bir uygulama olduğunu iddia edebilir, ancak aslında iç alay uygulaması da vardır. Ne zaman alay yazmaya başlarsanız, arayüzleri ayıklamanın mantıklı olduğunu göreceksiniz.

Fakat bekleyin, dahası var. Örtük arabirim uygulamalarının olduğu daha fazla senaryo vardır. Örneğin, .NET'in WCF iletişim yığını kullanılarak uzak bir servise bir proxy oluşturulur ve bu da arayüzü tekrar uygular.

Temiz bir kod ortamında, cevapların geri kalanına burada katılıyorum. Bununla birlikte, arayüzleri kullanabileceğiniz çerçevelere, kalıplara veya bağımlılıklara dikkat edin.


2
+1: Bir arayüze sahip olan ve arayüzleri kullanan çerçeveleri (örneğin JMock, vb.) Kullanmak size zaman kazandırabileceğinden, YAGNI'nin burada geçerli olduğundan emin değilim.
Deco

4
@Deco: İyi alaycı çerçeveler (aralarında JMock) bile arayüzlere ihtiyaç duymaz.
Michael Borgwardt

12
Yalnızca alaycı çerçevenizdeki bir sınırlama nedeniyle bir arayüz oluşturmak benim için çok kötü bir sebep gibi görünüyor. Örneğin, bir sınıfla alay etmek için EasyMock kullanmak zaten bir arayüz kadar kolaydır.
Alb

8
Bunun tersi olduğunu söyleyebilirim. Testte sahte nesnelerin kullanılması, tanımlara göre arayüzlerinize alternatif uygulamalar oluşturmak anlamına gelir. Bu, kendi FakeImplementation sınıflarınızı yaratmanız veya alaycı çerçevelerin sizin için ağır yük kaldırmasına izin verip vermediğiniz doğrudur. Somut sınıflarla alay etmek için çeşitli kesmeler ve düşük seviyeli püf noktaları kullanan EasyMock gibi bazı çerçeveler olabilir - ve onlara daha fazla güç! - ama kavramsal olarak, alay konusu nesneler bir sözleşmeye alternatif uygulamalardır.
Avner Shahar-Kashtan

1
Üretimdeki testlerden vazgeçmiyorsun. Arayüze ihtiyaç duymayan bir sınıf için neden bir arayüze ihtiyaç duyar?
Erik,

32

Hayır, onlara ihtiyacınız yok ve ben her sınıf referansı için otomatik olarak arayüzler yapmayı anti-pattern olarak görüyorum.

Her şey için Foo / FooImpl yapmanın gerçek bir maliyeti var. IDE ücretsiz olarak arayüz / uygulama yaratabilir, ancak koda giderken, istediğiniz foo.doSomething()gerçek uygulamaya değil, sizi arayüz imzasına götürmek için F3 / F12'den ekstra bilişsel yüke sahip olursunuz. Ayrıca her şey için bir dosya yerine iki dosya yığınına sahipsiniz.

Bu yüzden sadece bir şey için gerçekten ihtiyacınız olduğunda yapmalısınız.

Şimdi karşılıkları ele alıyoruz:

Bağımlılık enjeksiyon çerçeveleri için arayüzlere ihtiyacım var

Çerçeveleri destekleyen arayüzler eskidir. Java'da arayüzler, CGLIB öncesi dinamik proxy'ler için bir gereklilikti. Bugün, genellikle ona ihtiyacın yok. EJB3, Spring vb. Artık onlara ihtiyaç duymanıza gerek kalmayacağına, geliştirici üretkenliği için bir ilerleme ve nimet olarak kabul edilir.

Birim testi için alaycılara ihtiyacım var

Kendi alaylarınızı yazıp iki gerçek uygulamanız varsa, o zaman bir arayüz uygundur. Kod tabanınızda bir FooImpl ve bir TestFoo olsaydı muhtemelen bu tartışmayı ilk etapta yapmazdık.

Ancak, Moq, EasyMock veya Mockito gibi alaycı bir çerçeve kullanıyorsanız, sınıflarla alay edebilirsiniz ve arayüzlere ihtiyacınız yoktur. foo.method = mockImplementationYöntemlerin atanabileceği dinamik bir dilde ayarlamaya benzer .

Bağımlılık İnversiyon Prensibi'ni (DIP) takip etmek için arayüzlere ihtiyacımız var.

DIP, sözleşmelere (arayüzler) bağlı kaldığınızı ve uygulamalara bağlı olmadığını söylüyor. Ancak bir sınıf zaten bir sözleşme ve bir soyutlamadır. Genel / özel anahtar kelimeler bunun içindir. Üniversitede kanonik örnek bir Matris veya Polinom sınıfına benzer bir şeydi - tüketicilerin matrisler oluşturmak, eklemek, vb. İçin genel bir API'leri var, ancak matrisin seyrek veya yoğun biçimde uygulanıp uygulanmadığına bakmalarına izin verilmiyor. Bu noktayı kanıtlamak için IMatrix ya da MatrixImpl gerekli değildi.

Ayrıca, DIP genellikle yalnızca ana modül sınırlarında değil, her sınıf / yöntem çağrısı düzeyinde aşırı uygulanır. DIP'yi aşırı uyguladığınızın bir işareti, arabiriminizin ve uygulamanızın kilit adımda değiştiğinden, bir değişiklik yapmak için iki dosyaya dokunmanız gerekeceğidir. DIP uygun şekilde uygulanırsa, arayüzünüzün sık sık değişmemesi gerektiği anlamına gelir. Ayrıca, bir başka işaret, arayüzünüzün yalnızca bir gerçek tüketiciye sahip olduğudur (kendi uygulaması). Farklı bir hikaye, birçok farklı uygulamada tüketim için bir sınıf kitaplığı oluşturuyorsanız.

Bu Bob Amca'nın alay konusu ile ilgili noktalarının bir sonucudur - yalnızca büyük mimari sınırlarla alay etmeniz gerekir. Bir webapp'ta HTTP ve DB erişimi ana sınırlardır. Aradaki tüm sınıf / yöntem çağrıları değil. Aynı DIP için de geçerli.

Ayrıca bakınız:


4
Arabirimler yerine alaycı sınıflar (en azından Java ve C #), sahte nesnenin ortamla istenmeyen şekillerde etkileşime girmesine neden olabilecek üst sınıf yapıcının çalışmasını önlemenin bir yolu olmadığından kullanım dışı bırakılmalıdır. Arabirimi kopyalamak daha güvenli ve kolaydır, çünkü yapıcı kodu hakkında düşünmeniz gerekmez.
Jules

4
Alaycı sınıflarla ilgili herhangi bir sorunla karşılaşmadım, ancak IDE navigasyonunun beklendiği gibi çalışmaması beni hayal kırıklığına uğrattı. Gerçek bir problemi çözmek, varsayımsal olanı yener.
wrschneider,

1
@Jules Java'daki kurucusu da dahil olmak üzere somut bir sınıfla alay edebilirsiniz.
assylias

1
@assylias, yapıcının çalışmasını nasıl önlersiniz?
Jules

2
@Jules Bu alaycı çerçevenize bağlıdır - örneğin jmockit ile, sadece yazabilir new Mockup<YourClass>() {}ve bir arayüz, soyut bir sınıf veya somut bir sınıf olsun, yapıcıları da dahil olmak üzere tüm sınıfı alay edebilirsiniz. Yapmak isterseniz, yapıcı davranışını "geçersiz kılabilirsiniz". Sanırım Mockito veya Powermock'ta eşdeğer yollar var.
assylias

22

Çitin her iki tarafındaki cevaplar şöyle özetlenebilir:

İyi tasarlayın ve arayüzlerin gerekli olduğu arayüzleri yerleştirin.

Yanni'nin cevabına cevabımda belirttiğim gibi , arayüzler hakkında çok katı ve hızlı bir kuralın olabileceğini düşünmüyorum. Kural, tanım gereği esnek olmalıdır. Arayüzler konusundaki kuralım, bir arayüzün, API oluşturduğunuz her yerde kullanılması gerektiğidir. Ayrıca, sınırı bir sorumluluk alanından diğerine geçtiğiniz her yerde bir API oluşturulmalıdır.

(Korkunç biçimde tartışılan) bir örnek için, bir Carsınıf oluşturduğunuzu varsayalım. Sınıfınızda kesinlikle bir UI katmanına ihtiyacınız olacak. Bu özel örnekte, bir biçimini alır IginitionSwitch, SteeringWheel, GearShift, GasPedal, ve BrakePedal. Bu araba bir içerdiğinden, bir AutomaticTransmissionihtiyacınız yok ClutchPedal. (Ve bu korkunç bir araba olduğu için, klima, radyo ya da koltuk yok. Aslında, döşeme tahtaları da eksik.

Peki bu sınıflardan hangisinin bir arayüze ihtiyacı var? Cevap, tasarımınıza bağlı olarak hepsi ya da hiçbiri olabilir.

Şuna benzeyen bir arayüze sahip olabilirsiniz:

Interface ICabin
    Event IgnitionSwitchTurnedOn()
    Event IgnitionSwitchTurnedOff()
    Event BrakePedalPositionChanged(int percent)
    Event GasPedalPositionChanged(int percent)
    Event GearShiftGearChanged(int gearNum)
    Event SteeringWheelTurned(float degree)
End Interface

Bu noktada, bu sınıfların davranışı, ICabin Arayüzü / API'sinin bir parçası haline gelir. Bu örnekte, sınıflar (eğer varsa) muhtemelen birkaç özellik ve bir veya iki fonksiyonla basit. Ve tasarımınızla örtük olarak belirttiğiniz şey, bu sınıfların yalnızca sahip olduğunuz herhangi bir ICabin somut uygulamasını desteklemek için var olduğu ve kendi başlarına olamayacakları veya ICabin bağlamı dışında anlamsız olduklarıdır.

Özel üyelerin birim testini yapmamanızın nedeni aynıdır - bunlar yalnızca herkese açık API'yi desteklemek için vardır ve bu nedenle davranışları API'yi test ederek test edilmelidir.

Eğer sınıfınız yalnızca başka bir sınıfı desteklemek için mevcutsa ve kavramsal olarak onu gerçekten kendi etki alanına sahip olmadığını görüyorsanız , o zaman arayüzü atlamak iyi olur. Ancak, sınıfınız kendi etki alanı için yeterince büyüdüğünü düşündüğünüz ölçüde önemliyse, devam edin ve bir arabirim verin.


DÜZENLE:

Sık sık (bu cevaba dahil) 'domain', 'bağımlılık' (sıklıkla 'enjeksiyon' ile birleştiğinde) gibi programlamaya başladığınızda sizin için bir anlam ifade etmeyen şeyleri okuyacaksınız (kesinlikle benim için bir şey ifade). Etki alanı için, tam olarak neye benzediği anlamına gelir:

Egemenliğin veya otoritenin kullanıldığı bölge; egemen veya ortak bir mal varlığına veya benzerlerine sahip olmak. Figüratif olarak da kullanılır. [WordNet sense 2] [1913 Webster]

Benim örneğim açısından - en düşünelim IgnitionSwitch. Bir et otomobilinde, kontak anahtarı şunlardan sorumludur:

  1. Kullanıcının kimliğini doğrulama (tanımlama değil) (doğru anahtara ihtiyaçları var)
  2. Marş motoru için akım sağlanması, böylece gerçekten aracı çalıştırabilir.
  3. Ateşleme sistemine akım sağlanması, çalışmaya devam edebilmesi için
  4. Akımın kapatılması, böylece otomobil duracaktır.
  5. Nasıl gördüğünüze bağlı olarak, çoğu (hepsinde?) Yeni otomobillerde, şanzıman Park dışındayken anahtarın kontaktan çıkarılmasını önleyen bir anahtar vardır, bu nedenle bu etki alanının bir parçası olabilir. (Aslında sistemimi yeniden düşünmem ve yeniden tasarlamam gerektiği anlamına geliyor ...)

Bu özellikler oluşturan domain arasında IgnitionSwitchveya başka bir deyişle, ne bilir ve sorumludur.

Bu IgnitionSwitchsorumlu değildir GasPedal. Kontak anahtarı her yönden gaz pedalını tamamen görmezden gelir. Her ikisi de birbirinden tamamen bağımsız olarak çalışır (bir araba her ikisi de olmadan oldukça değersizdir!).

İlk başta belirttiğim gibi, tasarımınıza bağlı. IgnitionSwitchİki değeri olan birini tasarlayabilirsiniz : On ( True) ve Off ( False). Veya, kendisine sağlanan anahtarı ve başka bir dizi eylemi doğrulamak için tasarlayabilirsiniz. Bu, geliştirici olmanın zor kısmı, çizgileri kumda nereye çekeceğine karar vermektir - ve dürüst olmak gerekirse, çoğu zaman tamamen görecelidir. Kumdaki bu çizgiler yine de önemlidir - API'nizin olduğu ve dolayısıyla arayüzlerinizin olması gereken yer burasıdır.


Lütfen "kendi etki alanı var" derken neyi kastettiğinizi daha fazla açıklayabilir misiniz?
Lamin Sanneh

1
@LaminSanneh, ayrıntılı. Bu yardımcı olur mu?
Wayne Werner

8

Hayır (YAGNI) , bu arayüzü kullanarak diğer sınıflar için testler yazmayı planlamıyorsanız ve bu testler arayüzü alaydan faydalanabilir.


8

Gönderen MSDN :

Arabirimler, uygulamalarınızın, belirli işlevsellik sağlamak için muhtemelen ilgisiz birçok nesne türü gerektirdiği durumlar için daha uygundur.

Arayüzler temel sınıflardan daha esnektir, çünkü çoklu arayüzleri uygulayabilecek tek bir uygulama tanımlayabilirsiniz.

Arayüzler, uygulamayı bir temel sınıftan devralmanız gerekmediği durumlarda daha iyidir.

Arayüzler, sınıf mirasını kullanamadığınız durumlarda kullanışlıdır. Örneğin, yapılar sınıflardan miras alamazlar fakat arayüzleri uygulayabilirler.

Genel olarak, tek bir sınıf söz konusu olduğunda, bir arayüz uygulamak gerekli olmayacak, ancak projenizin geleceği göz önünde bulundurularak, sınıfların gerekli davranışlarını resmi olarak tanımlamak faydalı olabilir.


5

Soruyu cevaplamak için: Bundan daha fazlası var.

Bir arabirimin önemli bir yönü niyettir.

Arabirim "veri içermeyen, ancak davranışları ortaya çıkaran soyut bir tür" dür - Arayüz (hesaplama) Eğer bu bir davranış veya bir davranış dizisiyse, sınıfın desteklediği, bir arayüz muhtemelen doğru kalıp olabilir. Bununla birlikte, davranış (lar) sınıf tarafından somutlaştırılan konsepte özgüdürse, muhtemelen bir arayüz istemezsiniz.

Sorulması gereken ilk soru, temsil etmeye çalıştığınız şeyin ya da sürecin niteliğidir. Ardından, bu doğayı belirli bir şekilde uygulamak için pratik sebeplerle devam edin.


5

Bu soruyu sorduğunuzdan beri, birden çok uygulamayı gizleyen bir arayüze sahip olmanın ilgi alanlarını çoktan gördüğünüzü farz ediyorum. Bu bağımlılık inversiyon ilkesi ile kendini gösterebilir.

Bununla birlikte, bir ara yüze sahip olma ya da olmama zorunluluğu, uygulama sayısına bağlı değildir. Arayüzün asıl rolü, nasıl uygulanması gerektiği yerine hangi hizmetin verilmesi gerektiğini belirten bir sözleşmeyi tanımlamasıdır.

Sözleşme tanımlandıktan sonra iki veya daha fazla ekip bağımsız olarak çalışabilir. Modül A üzerinde çalıştığınızı ve B modülüne bağlı olduğunu, B'de bir arayüz oluşturmanın gerçeğinin, B'nin uygulaması hakkında endişelenmeden çalışmanıza devam etmesini sağladığını gösterir, çünkü tüm detaylar arayüz tarafından gizlenir. Böylece, dağıtılmış programlama mümkün hale gelir.

B modülü, arayüzünün sadece bir uygulaması olmasına rağmen, arayüz hala gereklidir.

Sonuç olarak, bir arayüz uygulama detaylarını kullanıcılarından gizlemektedir. Arayüze programlama daha fazla belge yazmaya yardımcı olur çünkü sözleşme tanımlanmalı, daha modüler yazılımlar yazılmalı, ünite testlerinin teşvik edilmesi ve geliştirme hızının arttırılması sağlanmalıdır.


2
Her sınıfın ortak arayüzü (ortak yöntemler) ve özel arayüzü (uygulama detayları) olabilir. Sınıfın ortak arayüzünün sözleşme olduğunu iddia edebilirim. Bu sözleşmede çalışmak için fazladan bir öğeye ihtiyacınız yok.
Fuhrmanator

4

Buradaki tüm cevaplar çok iyi. Aslında çoğu zaman farklı bir arayüz uygulamanıza gerek kalmaz . Ama durum vardır olabilir yine de yapmak istiyorum. İşte yaptığım bazı durumlar:

Sınıf,
Happen'i sık sık ortaya çıkarmak istemediğim başka bir arabirim uygular ve üçüncü taraf kodunu köprüleyen adaptör sınıfıyla birlikte olur.

interface NameChangeListener { // Implemented by a lot of people
    void nameChanged(String name); 
} 

interface NameChangeCount { // Only implemented by my class
    int getCount();
}

class NameChangeCounter implements NameChangeListener, NameChangeCount {
    ...
}

class SomeUserInterface {
    private NameChangeCount currentCount; // Will never know that you can change the counter
}

Sınıf,
çoğunlukla dış kütüphanelerle etkileşime girdiğinde sızıntı yapmaması gereken belirli bir teknolojiyi kullanır . Tek bir uygulama olsa bile, harici kütüphane ile gereksiz eşleştirme yapmadığımdan emin olmak için bir arayüz kullanıyorum.

interface SomeRepository { // Guarantee that the external library details won't leak trough
    ...
}

class OracleSomeRepository implements SomeRepository { 
    ... // Oracle prefix allow us to quickly know what is going on in this class
}

Çapraz katman iletişimi
Yalnızca bir UI sınıfı etki alanı sınıfından birini uygulasa bile, bu katman arasında daha iyi ayrım yapılmasına izin verir ve en önemlisi döngüsel bağımlılığı önler.

package project.domain;

interface UserRequestSource {
    public UserRequest getLastRequest();
}

class UserBehaviorAnalyser {
    private UserRequestSource requestSource;
}

package project.ui;

class OrderCompleteDialog extends SomeUIClass implements project.domain.UserRequestSource {
    // UI concern, no need for my domain object to know about this method.
    public void displayLabelInErrorMode(); 

    // They most certainly need to know about *that* though
    public UserRequest getLastRequest();
}

Yöntemin yalnızca bir alt kümesi çoğu nesneye uygun olmalıdır.
Çoğunlukla somut sınıfta bazı yapılandırma yöntemlerine sahipsem

interface Sender {
    void sendMessage(Message message)
}

class PacketSender implements Sender {
    void sendMessage(Message message);
    void setPacketSize(int sizeInByte);
}

class Throttler { // This class need to have full access to the object
    private PacketSender sender;

    public useLowNetworkUsageMode() {
        sender.setPacketSize(LOW_PACKET_SIZE);
        sender.sendMessage(new NotifyLowNetworkUsageMessage());

        ... // Other details
    }
}

class MailOrder { // Not this one though
    private Sender sender;
}

Sonunda, özel alan kullanmamla aynı sebepten dolayı arayüzü kullanıyorum: diğer nesnenin erişmemesi gereken şeylere erişimi olmamalı. Böyle bir durum varsa, sadece bir sınıf uygulasa bile bir arayüz tanıtırım .


2

Arayüzler gerçekten önemlidir, ancak sahip olduğunuz sayısını kontrol etmeye çalışın.

Hemen hemen her şey için arayüz oluşturma yoluna girdikten sonra 'doğranmış spagetti' koduyla bitmek kolaydır. Konuyla ilgili çok akıllıca sözler yazan Ayende Rahien'in bilgeliğini erteliyorum:

http://ayende.com/blog/153889/limit-your-abstractions-analyzing-a-ddd-application

Bu onun bütün dizinin ilk yayınıdır, okumaya devam et!


'doğranmış spagetti' kodu mantı kodu olarak da bilinir c2.com/cgi/wiki?RavioliCode
CurtainDog

Bana çok lazanya kodu veya baklava kodu gibi geliyor - çok fazla katman içeren kod. ;-)
dodgy_coder

2

Bu durumda hala bir arayüz tanıtmak isteyebileceğiniz bir neden, Bağımlılık İnversiyon Prensibi'ni takip etmektir . Yani, sınıfı kullanan modül, somut bir uygulamaya bağlı olmak yerine bunun bir soyutlamasına (yani arayüz) bağlı olacaktır. Yüksek seviye bileşenleri düşük seviye bileşenlerden ayırır.


2

Hiçbir şey yapmanın gerçek bir nedeni yok. Arayüzler çıktı programını değil size yardımcı olmak içindir. Arayüz bir milyon sınıf tarafından uygulansa bile, bir tane oluşturmanız gerektiğini söyleyen bir kural yoktur. Siz ya da kodunuzu kullanan bir başkası, onun tüm uygulamalarıyla örtüşen bir şeyi değiştirmek istediğinde, bir tane yaratırsınız. Arabirim oluşturmak, onu uygulayacak başka bir sınıf oluşturmak isteyebileceğiniz tüm gelecekteki durumlarda size yardımcı olacaktır.


1

Bir sınıf için bir arayüz tanımlamaya her zaman gerek yoktur.

Değer nesneleri gibi basit nesnelerin birden fazla uygulaması yoktur. Onların da alay edilmelerine gerek yok. Uygulama kendi başına test edilebilir ve bunlara bağlı diğer sınıflar test edildiğinde gerçek değer nesnesi kullanılabilir.

Bir arayüz oluşturmanın bir maliyeti olduğunu unutmayın. Uygulama boyunca güncellenmesi gerekiyor, fazladan bir dosyaya ihtiyacı var ve bazı IDE arabirimde değil uygulamada yakınlaştırma konusunda sorun yaşayacak.

Bu yüzden arayüzleri sadece uygulamadan bir soyutlama yapmak istediğiniz yüksek seviyeli sınıflar için tanımlayacağım.

Bir sınıfla ücretsiz olarak bir arayüz elde edebileceğinizi unutmayın. Uygulamanın yanı sıra, bir sınıf, kamu yöntem kümesinden bir arayüz tanımlar. Bu arayüz türetilmiş tüm sınıflar tarafından uygulanır. Kesinlikle bir arayüzden bahsetmiyor, fakat aynı şekilde kullanılabilir. Bu yüzden, sınıf adı altında zaten var olan bir arayüzü yeniden yaratmanın gerekli olduğunu düşünmüyorum.

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.