Arayüz kullanmamak kötü alışkanlık mıdır? [kapalı]


40

Arayüzleri nadiren kullanıyorum ve başkalarının kodunda ortak buluyorum.

Ayrıca nadiren kodumda alt ve süper sınıflar (kendi sınıflarımı oluştururken) oluşturuyorum.

  • Kötü bir şey mi?
  • Bu tarz değiştirmeyi önerir misiniz?
  • Bu tarzın herhangi bir yan etkisi var mı?
  • Bunun nedeni büyük projeler üzerinde çalışmadığımdan mı?

10
Bu hangi dil?

2
Hangi dilin yanında, hangi paradigma nedir?
Armando

1
“Arayüz” kavramı dili aşmaz mı? Java yerleşik Arabirim türüne sahiptir, ancak C ++ geliştiricileri de genellikle arabirimler oluşturur (öneklerini I ile ön ekler) ve hepsi aynı amaca hizmet eder.
Ricket

4
@ Ricket: Hayır, pek değil. Sorun, C ++ 'nın çoklu sınıf mirası sunmasıdır. C ++ 'da bir "arabirim" çok daha kavramsaldır ve gerçekte çoğunlukla soyut bir temel sınıf olarak tanımladıklarını kullanırız. #
DeadMG

2
@Ricket no, özellikle prosedürel veya OOP dilinden ziyade işlevsel bir dilde çalışıyorsanız.
alternatif

Yanıtlar:


36

Arabirimleri kullanmak isteyebileceğiniz birkaç neden var:

  1. Arabirimler, uygulamalarınızın belirli işlevsellik sağlamak için muhtemelen ilgisiz birçok nesne türü gerektirdiği durumlar için uygundur.
  2. Arayüzler temel sınıflardan daha esnektir, çünkü çoklu arayüzleri uygulayabilecek tek bir uygulama tanımlayabilirsiniz.
  3. Arayüzler, uygulamayı bir temel sınıftan devralmanız gerekmediği durumlarda daha iyidir.
  4. 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.

http://msdn.microsoft.com/en-us/library/3b5b8ezk(v=vs.80).aspx

Arayüzler programlamada başka herhangi bir şey gibidir. Onlara ihtiyacın yoksa, kullanmayın. Bunları çok yaygın bir stil meselesi olarak kullandıklarını gördüm, ancak bir arayüzün sağladığı özel özelliklere ve özelliklere ihtiyacınız yoksa, bunları yalnızca “çünkü” kullanmanın faydasını görmüyorum.


13
Dili belirtilmemiş ve sizinki çok özel; örneğin, C ++ 'da, bir yapı gerçekten bir sınıftan miras alabilir ve soyut olmayan temelleri miras ile çarpabilirsiniz.
DeadMG

2
Bu cevap C # gibi görünüyor ama aynı zamanda # 4'ü alırsanız Java ile de ilgili ve C ++ 'a birden fazla kalıtıma izin verildiğinden dolayı sadece C ++ için geçerli.
Ricket

2
Ayrıca, son ifadeye katılmıyorum. Bence programcıların yapması gereken çok sayıda iyi uygulama var ama yapmamalılar çünkü onlara ihtiyaç duymadıklarını düşünüyorlar. Bence, etrafa bakmakta, herkesin bu şeyi yaptığını görmekte ve belki de onun da yapması gerektiğini ama önce "neden" diye sorması gerektiğini düşünüyorum.
Ricket

3
@Ricket: Neden cevabımın dört kurşun noktasında. Bu nedenlerden hiçbiri geçerli değilse, bir arayüze ihtiyacınız yoktur.
Robert Harvey,

17

Hem sınıf mirası hem de arayüzlerin her ikisi de kendi yerine sahiptir. Kalıtım, "bir" anlamına gelirken, arayüz, "neye benzeyeceğini" tanımlayan bir sözleşme sağlar.

Arayüzleri daha sık kullanmanın hiç de kötü bir uygulama olmadığını söyleyebilirim. Şu anda Bill Wagner tarafından "Etkili C # - 50 C #'nuzu Geliştirmenin Özel Yolları" nı okuyorum. 22 numaralı madde, ve "Mirasa Arayüzleri Tanımlama ve Uygulama" tercihini belirtir.

Genel olarak, kavramsal olarak ilişkili türler arasında belirli bir ortak davranış uygulaması tanımlamam gerektiğinde temel sınıfları kullanırım. Daha sık arayüzleri kullanıyorum. Aslında, normalde bir sınıf oluşturmaya başladığımda bir sınıf için bir arayüz tanımlayarak başlıyorum ... sonunda arayüzü derlemesem bile, bunun genel API'sini tanımlayarak başlamak için yardımcı olduğunu buluyorum. Baştan beri sınıf. Hem arayüzü uygulayan hem de uygulama mantığının aynı olduğu birden fazla sınıfa sahip olduğumu tespit edersem, ancak o zaman türler arasında ortak bir temel sınıf uygulamanın mantıklı olup olmadığını kendime soracağım.

Bill Wagners kitabından birkaç alıntı ...

türetilmiş tüm sınıflar hemen bu davranışı içerir. Bir arabirime üye eklemek, bu arabirimi uygulayan tüm sınıfları keser. Yeni yöntemi içermeyecekler ve artık derlenmeyeceklerdir. Her uygulayıcı, yeni üyeyi dahil etmek için bu türü güncellemelidir. Soyut bir temel sınıf ve bir arayüz arasında seçim yapmak, soyutlamalarınızı zaman içinde en iyi şekilde nasıl destekleyeceğiniz sorusudur. Arayüzler sabittir: Herhangi bir türün uygulayabileceği bir dizi işlevsellik için arayüzü bir sözleşme olarak yayınlarsınız. Temel sınıflar zamanla uzatılabilir. Bu uzantılar türetilmiş her sınıfın bir parçası haline gelir. İki model, çoklu arabirimleri desteklerken uygulama kodunu yeniden kullanmak için karıştırılabilir. " Yeni yöntemi içermeyecekler ve artık derlenmeyeceklerdir. Her uygulayıcı, yeni üyeyi dahil etmek için bu türü güncellemelidir. Soyut bir temel sınıf ve bir arayüz arasında seçim yapmak, soyutlamalarınızı zaman içinde en iyi şekilde nasıl destekleyeceğiniz sorusudur. Arayüzler sabittir: Herhangi bir türün uygulayabileceği bir dizi işlevsellik için arayüzü bir sözleşme olarak yayınlarsınız. Temel sınıflar zamanla uzatılabilir. Bu uzantılar türetilmiş her sınıfın bir parçası haline gelir. İki model, çoklu arabirimleri desteklerken uygulama kodunu yeniden kullanmak için karıştırılabilir. " Yeni yöntemi içermeyecekler ve artık derlenmeyeceklerdir. Her uygulayıcı, yeni üyeyi dahil etmek için bu türü güncellemelidir. Soyut bir temel sınıf ve bir arayüz arasında seçim yapmak, soyutlamalarınızı zaman içinde en iyi şekilde nasıl destekleyeceğiniz sorusudur. Arayüzler sabittir: Herhangi bir türün uygulayabileceği bir dizi işlevsellik için arayüzü bir sözleşme olarak yayınlarsınız. Temel sınıflar zamanla uzatılabilir. Bu uzantılar türetilmiş her sınıfın bir parçası haline gelir. İki model, çoklu arabirimleri desteklerken uygulama kodunu yeniden kullanmak için karıştırılabilir. " Herhangi bir türün uygulayabileceği bir işlevsellik kümesi için arayüzü bir sözleşme olarak yayınlarsınız. Temel sınıflar zamanla uzatılabilir. Bu uzantılar türetilmiş her sınıfın bir parçası haline gelir. İki model, çoklu arabirimleri desteklerken uygulama kodunu yeniden kullanmak için karıştırılabilir. " Herhangi bir türün uygulayabileceği bir işlevsellik kümesi için arayüzü bir sözleşme olarak yayınlarsınız. Temel sınıflar zamanla uzatılabilir. Bu uzantılar türetilmiş her sınıfın bir parçası haline gelir. İki model, çoklu arabirimleri desteklerken uygulama kodunu yeniden kullanmak için karıştırılabilir. "

"Kodlama arayüzleri, diğer geliştiricilere baz sınıf tiplerine kodlamadan daha fazla esneklik sağlar."

"Bir sınıf için API tanımlamak için arayüzleri kullanmak daha fazla esneklik sağlar."

"Türünüz özellikleri sınıf türleri olarak gösterdiğinde, arayüzün tamamını o sınıfa gösterir. Arabirimleri kullanarak, yalnızca istemcilerin kullanmasını istediğiniz yöntemleri ve özellikleri göstermeyi seçebilirsiniz."

"Temel sınıflar, ilgili beton türleri arasındaki ortak davranışları tanımlar ve uygular. Arayüzler, ilişkisiz beton türlerinin uygulayabileceği atomik işlevsellik parçalarını tanımlar. Her ikisi de kendi yerlerine sahiptir. Sınıflar oluşturduğunuz türleri tanımlar. Arabirimler, bu türlerin davranışını işlevsellik parçaları olarak tanımlar. Farklılıkları anlarsanız, değişim karşısında daha dayanıklı olan daha etkileyici tasarımlar yaratırsınız. İlgili türleri tanımlamak için sınıf sıradüzenlerini kullanın. Bu türlerde uygulanan arayüzleri kullanarak işlevselliği gösterin. "


2
Ben okudum ve iyi bir cevap olduğunu düşündüm.
Eric King,

1
@ mc10 Sorunun ne olduğundan emin değil. Bir kitaba atıfta bulundu ve cevabının alt yarısı, zamanınızı boşa harcamak istememeniz durumunda açıkça bilmenizi sağlayan kitaptan alıntılar.
Pete

@Pete Cevabın kötü olduğunu söylemedim, ama sadece biraz fazla olduğunu söyledi. Bazen bütün teklife ihtiyacın olduğundan şüpheliyim.
kevinji

3
haha, eğer bu tl; dr, tüm bu StackExchange-yüksek-kalite-cevaplar olayını seveceğinizden emin değilim.
Nic

haha, teşekkürler beyler. Evet, cevabın biraz uzun sürdüğü aklıma geldi, ama cevabımı nitelendirebileceğimi düşündüm, Wagner’in, ara yüzlere karşı mirasa ilişkin avantaj ve dezavantajların yanı sıra kalıtım ile ilgili senaryoları da içerecek her biri daha uygundur. Belki de doğrudan kitaptan alıntı yapmak cevabıma çok fazla değer katmamıştır.
John Connelly

8

Test ediyor söz edilmemiştir bir şey var: bütün C # alaycı-kütüphanelerde yanı Java için bazı olarak, sınıflar olamaz onlar bir arabirim uygulamadığınız sürece alay. Bu, her sınıfa kendi arayüzünü vermek için Çevik / TDD uygulamalarını takip eden birçok projeye öncülük eder.

Bazı insanlar bu en iyi uygulamayı düşünür, çünkü "eşleşmeyi azaltır", ancak ben aynı fikirde değilim - bence bu sadece dildeki bir eksiklik için geçici bir çözüm.


Bence arayüzler, soyut olarak “aynı şeyi” yapan ancak farklı şekillerde iki veya daha fazla sınıfınız olduğunda kullanılır.

Örneğin .Net çerçevesi, malzeme listelerini depolayan birden fazla sınıfa sahiptir , ancak hepsi bu öğeleri farklı şekillerde depolar. Bu nedenle, IList<T>farklı yöntemler kullanılarak uygulanabilecek soyut bir arayüze sahip olmak mantıklıdır .

Gelecekte 2+ sınıfın birbiriyle değiştirilebilir veya değiştirilebilir olmasını istediğinizde de kullanmalısınız. Gelecekte listelerin depolanmasının yeni bir yolu ortaya çıkarsa , kodunuz boyunca AwesomeList<T>kullandığınızı varsayarsak IList<T>, kullanımı AwesomeList<T>değiştirmek, birkaç yüz / bin yerine yalnızca birkaç düzine satırı değiştirmek anlamına gelir.


5

Kalıtım ve ara yüzlerin uygun olmayan hallerde kullanılmamasının ana sonucu sıkı bağlantıdır . Bunu tanımayı öğrenmek biraz zor olabilir, ancak genellikle en belirgin belirti, değişiklik yaptığınızda, bir dalgalanma efektinde değişiklik yapmak için sık sık bir sürü başka dosyadan geçmek zorunda kalmanızdır.


4

Hayır, bu hiç fena değil. Açık arayüzler kullanılmalı ve eğer sadece ve eğer ortak bir arayüze sahip iki sınıfın çalışma zamanında değiştirilebilir olması gerekiyorsa kullanılmalıdır. Değiştirilebilir olmaları gerekmiyorsa, onları devralmalarını isteme. Bu kadar basit. Kalıtım kırılgandır ve mümkün olduğunda kaçınılmalıdır. Bundan kaçınabiliyorsanız veya bunun yerine jenerik kullanıyorsanız, yapın.

Buradaki sorun, zayıf derleme zamanı jenerikliğine sahip C # ve Java gibi dillerde, tüm sınıflar aynı temelden devralmadıkça birden fazla sınıfla ilgilenebilecek bir yöntem yazmanın bir yolu olmadığından DRY'yi ihlal etmeye son verebilirsiniz. Bununla birlikte, C # 4'ler bununla baş dynamic edebilir .

Mesele şu ki miras, global değişkenler gibidir - bir kere eklediğinizde ve kodunuz buna bağlı olarak, Tanrı onu götürmenize yardım eder. Ancak, istediğiniz zaman ekleyebilir ve hatta bir çeşit sarıcı kullanarak taban sınıfını değiştirmeden bile ekleyebilirsiniz.


1
Oy kullanma sebebi?
DeadMG

Emin değilim, bir artı değeriniz olsun. Güzel bir cevaptı.
Bryan Boettcher

3

Evet, (veya çok nadiren) arayüzleri kullanmak muhtemelen kötü bir şeydir. Arayüzler (soyut anlamda ve C # / Java dil yapısı o soyut duyunun oldukça iyi bir yaklaşımıdır) sistemler ve alt sistemler arasındaki açık etkileşim noktalarını tanımlar. Bu, kuplajı azaltmaya yardımcı olur ve sistemi daha iyi bakım yapar. Sürdürülebilirliği artıran her şeyde olduğu gibi, sistem ne kadar büyükse o kadar önemli hale gelir.


3

Yıllardır arayüz kullanmadım. Tabii ki bu, yıllardır neredeyse sadece Erlang'da programlama yapıyorum ve bir arabirimin bütün konsepti sadece mevcut değil. (Alacağınız yakın bir "davranış" olduğunu ve gerçekten zor şaşı sürece bu gerçekten aynı şey değil ve dışarı göz ucuyla onlara bir bak.)

Bu yüzden, gerçekten, sorunuz paradigmaya bağlıdır (bu durumda OOP) ve dahası, gerçekten oldukça dile bağlıdır (arayüzsüz OOP dilleri vardır).


2

Java kullanmaktan bahsediyorsanız, arabirimleri kullanmanın bir nedeni, proxy nesnelerinin kod oluşturma kitaplıkları olmadan kullanılmasını sağlamalarıdır. Spring gibi karmaşık bir çerçeveyle çalışırken bu önemli bir avantaj olabilir. Dahası, bazı işlevler arabirimler gerektirir : RMI, bunun klasik bir örneğidir; sağladığınız işlevselliği arabirimler (bunları devralan java.rmi.Remote) olarak tanımlamanız gerekir, ancak bunları uygulamaya devam edersiniz.


1

Her şey değişiyor ( MS Moles ), ancak neredeyse sadece arayüzlere kodlamanın iyi olacağını düşünmemin temel nedeni, bir IoC mimarisine doğal olarak takılmaları ve doğal olarak uymaları kolay olmaları.

IMO, yalnızca arabirimlerle çalışmalı veya DAO'ları mümkün olduğunca tamamen aptallaştırmalısınız. Bu zihniyete girdikten sonra, kendisini arayüzlerle göstermeyen ve dürüst olmak zorunda olduğum somut nesnelerle her şeyi yapan bir kütüphaneyi kullanmaya başladığınızda, hepsi biraz garip geliyor.


0

Eski moda projeler için, dairesel referanslardan kaçınmak için arayüzler kullanılır, çünkü dairesel referanslar uzun vadede büyük bakım sorunu olabilir.

Kötü:

class B; // forward declaration
class A
{
  B* b;
};

class B
{
  A* a;
}

Fena değil:

class PartOfClassB_AccessedByA
{
};

class A
{
  PartOfClassB_AccessedByA* b;
};

class B : public PartOfClassB_AccessedByA
{
  A* a;
}

Genellikle A, B, PartOfClassB_AccessedByA ayrı dosyalar ile uygulanır.


0

Arayüz tabanlı programlama, kodu daha esnek ve test etmeyi kolaylaştırmaya yardımcı olur. Uygulama sınıfları, müşteri koduna dokunmadan değiştirilebilir [Esneklik]. Kodları test ederken, gerçek arayüz tabanlı programlamanın yerine geçmesi, kodun daha esnek ve test edilmesinin daha kolay olmasına yardımcı olur. Uygulama sınıfları, müşteri koduna dokunmadan değiştirilebilir [Esneklik]. Kod test edilirken, gerçek nesneyi sahte nesnelerle değiştirebilir [Testability] .bject sahte nesnelerle [Testability].

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.