Arayüzler - Amaç nedir?


269

Arayüzlerin nedeni beni gerçekten atlatıyor. Anladığım kadarıyla, C # 'da olmayan varoluşsal çoklu miras için bir çalışmadır (ya da öyle söylendi).

Tüm gördüğüm, daha sonra sınıfta yeniden tanımlanması gereken bazı üyeleri ve işlevleri önceden tanımlamanızdır. Böylece arayüzü gereksiz kılar. Sadece sözdizimsel gibi hissettiriyor… iyi, bana önemsiz (Lütfen hiçbir suç anlamına gelmez. Gereksiz şeylerde olduğu gibi önemsiz).

Aşağıda yığın taşması farklı bir C # arabirim iş parçacığından alınan örnekte, ben sadece bir arabirim yerine Pizza adlı bir temel sınıf oluşturmak istiyorum.

kolay örnek (farklı bir yığın taşması katkısından alınmıştır)

public interface IPizza
{
    public void Order();
}

public class PepperoniPizza : IPizza
{
    public void Order()
    {
        //Order Pepperoni pizza
    }
}

public class HawaiiPizza : IPizza
{
    public void Order()
    {
        //Order HawaiiPizza
    }
}

SO burada bu sorunun kopyaları olduğunu hissediyorum, ama hepsi sadece bir arayüz sözleşme kısmını açıklamak gibi görünüyor bu yüzden onlar emin değilim.
Lasse V. Karlsen

7
hoş ve düzenli bir kullanıcı olmaya çalışırken, bir şey göndermeden önce çeşitli forumlarda cevabımı arama eğilimindeyim. Ne yazık ki, çoğu daha sonraki bir aşamada başladı ve geri kalanı yardımcı olmadı. Zaten temel "Neden yapıyorsun?" bana gereksiz yere aşırı karmaşık gibi görünüyordu. Btw. Çok hızlı cevaplar için herkese teşekkürler. Önce hepsini sindirmeliyim, ama sanırım şimdi onların amacı hakkında oldukça iyi bir fikrim var. Görünüşe göre hep farklı bir açıdan baktım. Yardımın için çok teşekkürler.
Nebelhom


1
Ayrıca arayüzler, structtipler gibi kalıtımın oluşturulmasına yardımcı olur .
ja72

3
Hmm, OP, "Anladığım kadarıyla, arayüz, C # 'da olmayan varolmayan çoklu kalıtım için bir çeşit çalışmadır. (Bunun dışında, alıntılanan ders kitabı pizza örneğinde) msgstr "arabirim yerine temel sınıf kullan" #:. Ve sonra cevapların çoğu ya bir (soyut) temel sınıf tarafından uygulanabilecek bir örnek verdi ya da çoklu kalıtım senaryosu için arayüzün nasıl gerekli olduğunu gösteren bir örnek verdi. Bu cevapların hepsi iyi, ama sadece OP'nin zaten bildiği bir şeyi tekrarlamakla kalmıyorlar mı? OP'nin örneksiz bir cevap seçmesine şaşmamalı. LOL
RayLuo

Yanıtlar:


176

Mesele şu ki, arayüz bir sözleşmeyi temsil ediyor . Herhangi bir uygulayıcı sınıfın sahip olması gereken bir dizi kamusal yöntem. Teknik olarak, arayüz sadece sözdizimini, yani hangi yöntemlerin olduğunu, hangi argümanları aldıklarını ve ne döndüklerini yönetir. Genellikle sadece semantikleri de içine alırlar, ancak bu sadece dokümantasyon ile.

Daha sonra bir arayüzün farklı uygulamalarına sahip olabilir ve bunları istediğiniz zaman değiştirebilirsiniz. Örneğinizde, her pizza örneği bir olduğu için bilinmeyen bir pizza türü örneğini işlediğiniz her yerde IPizzakullanabilirsiniz IPizza. Türü IPizzadevralınan herhangi bir örnek , bir Order()yönteme sahip olduğu için düzenlenebilir olması garantilidir .

Python statik olarak yazılmamıştır, bu nedenle türler çalışma zamanında tutulur ve aranır. Böylece Order()herhangi bir nesne üzerinde yöntem çağırmayı deneyebilirsiniz . Çalışma zamanı, nesnenin böyle bir yöntemi olduğu sürece mutlu olur ve muhtemelen sadece omuz silkiyor ve yoksa "Meh." C # 'da öyle değil. Derleyici doğru çağrıları yapmaktan sorumludur ve eğer rasgele varsa object, derleyici henüz çalışma zamanında örneğin bu yönteme sahip olup olmadığını bilmez. Derleyicinin bakış açısından, doğrulayamadığından geçersizdir. (Bu tür şeyleri yansıma veya dynamicanahtar kelime ile yapabilirsiniz, ancak bu şu an biraz ileri gidiyor, sanırım.)

Ayrıca, olağan anlamda bir arayüzün mutlaka C # olması gerekmediğini, interfacesoyut bir sınıf veya hatta normal bir sınıf olabileceğini unutmayın (tüm alt sınıfların bazı ortak kodları paylaşması gerekiyorsa kullanışlı olabilir - çoğu durumda ancak interfaceyeterlidir).


1
+1, ancak arabirimin (bir sözleşme anlamında) soyut veya normal bir sınıf olabileceğini söylemezdim.
Groo

3
Birkaç dakika içinde arayüzleri anlamayı bekleyemeyeceğinizi de ekleyeceğim. Yıllarca nesne yönelimli programlama deneyiminiz yoksa arayüzleri anlamanın makul olmadığını düşünüyorum. Kitaplara bazı bağlantılar ekleyebilirsiniz. Şunu öneririm: Sadece hafif bir giriş değil, alışkanlık deliğinin ne kadar derine inandığı .NET'te Bağımlılık Enjeksiyonu .
knut

2
Ah, DI ile ilgili hiçbir ipucum yok. Ama sanırım askerin asıl problemi Python'da neden olmadan çalıştıklarına neden olmalarıydı. Cevabımın en büyük paragrafı bunu sağlamaya çalıştı. Burada arayüzler kullanan her modeli ve uygulamayı kazmanın gerekli olduğunu düşünmüyorum.
Joey

1
O zaman sorunuz şu şekilde olur: "Dinamik olarak yazılan diller programlamak daha kolay olduğunda neden statik olarak yazılan dilleri kullanıyorsunuz?". Bu soruyu cevaplayacak uzman olmasam da, performansın belirleyici konu olduğunu söyleyebilirim. Bir python nesnesine çağrı yapıldığında, işlemin çalışma sırasında bu nesnenin "Order" adlı bir yöntemi olup olmadığına karar vermesi gerekirken, bir C # nesnesine çağrı yaparsanız, bu yöntemi uyguladığı ve böyle bir adrese çağrı yapılabilir.
Boluc Papuccuoglu

3
@BolucPapuccuoglu: Bunun ötesinde, eğer bir kişi foouygularsa IWidget, statik olarak yazılmış bir nesne ile , bir çağrıyı gören bir programcı foo.woozle()belgelere bakabilir IWidgetve bu yöntemin ne yapması gerektiğini bilir . Programcı, gerçek uygulama kodunun nereden geleceğini bilmenin bir yolu olmayabilir, ancak IWidgetarayüz sözleşmesine uyan her tür foo, bu sözleşmeyle tutarlı bir şekilde uygulanacaktır . Dinamik bir dilde, aksine, ne foo.woozle()anlama geleceğine dair net bir referans noktası olmayacaktır .
supercat

440

Kimse arayüzlerin ne kadar yararlı olduğunu açık bir şekilde açıklamamıştı, bu yüzden denemek istiyorum (ve Shamim'in cevabından biraz bir fikir çalacağım).

Bir pizza sipariş servisi fikrini ele alalım. Birden fazla pizza türüne sahip olabilirsiniz ve her pizza için ortak bir eylem sistemdeki siparişi hazırlamaktadır. Her pizza hazırlanmalı, ancak her pizza farklı hazırlanıyor . Örneğin, bir doldurulmuş kabuk pizza sipariş edildiğinde, sistem muhtemelen restoranda belirli malzemelerin mevcut olduğunu doğrulamak ve bunları derin yemek pizzaları için gerekli olmayan bir kenara koymak zorundadır.

Bunu kodda yazarken, teknik olarak

public class Pizza()
{
    public void Prepare(PizzaType tp)
    {
        switch (tp)
        {
            case PizzaType.StuffedCrust:
                // prepare stuffed crust ingredients in system
                break;

            case PizzaType.DeepDish:
                // prepare deep dish ingredients in system
                break;

            //.... etc.
        }
    }
}

Bununla birlikte, derin tabak pizzaları (C # terimleriyle), Prepare() yöntemde doldurulmuş kabuktan ve bu nedenle çok sayıda isteğe bağlı özellik elde edersiniz ve sınıf iyi ölçeklenmez (yeni eklerseniz pizza türleri).

Bunu çözmenin uygun yolu arayüzü kullanmaktır. Arayüz, tüm Pizzaların hazırlanabileceğini, ancak her pizzanın farklı şekilde hazırlanabileceğini beyan eder. Yani aşağıdaki arayüzlere sahipseniz:

public interface IPizza
{
    void Prepare();
}

public class StuffedCrustPizza : IPizza
{
    public void Prepare()
    {
        // Set settings in system for stuffed crust preparations
    }
}

public class DeepDishPizza : IPizza
{
    public void Prepare()
    {
        // Set settings in system for deep dish preparations
    }
}

Artık sipariş işleme kodunuzun, malzemeleri işlemek için hangi pizza türlerinin sipariş edildiğini tam olarak bilmesine gerek yok. Sadece var:

public PreparePizzas(IList<IPizza> pizzas)
{
    foreach (IPizza pizza in pizzas)
        pizza.Prepare();
}

Her bir pizza türü farklı hazırlanmış olsa da, kodun bu kısmı ne tür bir pizza ile uğraştığımızı önemsemek zorunda değildir, sadece pizzalar için çağrıldığını bilir ve bu nedenle Prepareher bir pizza her otomatik olarak doğru şekilde hazırlanır koleksiyonunda birden fazla pizza türü olsa bile.


11
+1, bir arayüzün kullanımını göstermek için bir Liste kullanma örneğini beğendim.
danielunderwood

21
İyi cevap, ama belki de bu durumda bir arabirimin neden soyut bir sınıf kullanmaktan daha iyi olduğunu açıklığa kavuşturmak (böyle basit bir örnek için soyut bir sınıf gerçekten daha iyi olabilir mi?)
Jez

3
Bu en iyi cevap. Küçük bir yazım hatası var ya da sadece kodu kopyalayıp yapıştırabilirsiniz. C # arayüzünde, gibi erişim değiştiricileri bildirmezsiniz public. Yani arayüzün içinde sadecevoid Prepare();
Dush

26
Bunun soruyu nasıl cevapladığını anlamıyorum. Bu örnek, asıl sorunun işaret ettiği temel sınıf ve soyut bir yöntemle kolayca yapılabilirdi.
Jamie Kitson

4
Arayüzler, birçoğu etrafta dolaşana kadar gerçekten sağlarına girmiyor. Soyut olsun ya da olmasın yalnızca tek bir sınıftan miras alabilirsiniz, ancak tek bir sınıfa istediğiniz kadar farklı arabirim uygulayabilirsiniz. Aniden bir kapı kasasında bir kapıya ihtiyacınız yok; IOpenable olan herhangi bir şey, bir kapı, bir pencere, bir mektup kutusu, bunu adlandırır.
mtnielsen

123

Benim için, bu noktaya gelindiğinde, sadece kodunuzu yazmayı daha kolay / hızlı hale getirmek için bir şeyler olarak bakmayı bıraktığınızda netleşti - bu onların amacı değil. Birkaç kullanımları vardır:

(Bu, pizza benzetmesini kaybedecek, çünkü bunun kullanımını görselleştirmek çok kolay değil)

Ekranda basit bir oyun yaptığınızı ve etkileşimde bulunduğunuz yaratıkların olacağını söyleyin.

C: Ön uç ile arka uç uygulamanız arasında gevşek bir bağlantı sağlayarak kodunuzun gelecekte korunmasını kolaylaştırabilirler.

Başlamak için bunu yazabilirsiniz, çünkü sadece troller olacak:

// This is our back-end implementation of a troll
class Troll
{
    void Walk(int distance)
    {
        //Implementation here
    }
}

Başlangıç ​​aşaması:

function SpawnCreature()
{
    Troll aTroll = new Troll();

    aTroll.Walk(1);
}

İki hafta boyunca, pazarlama, Twitter'da onlar hakkında okudukları için Orklara da ihtiyacınız olduğuna karar verir, böylece aşağıdaki gibi bir şey yapmanız gerekir:

class Orc
{
    void Walk(int distance)
    {
        //Implementation (orcs are faster than trolls)
    }
}

Başlangıç ​​aşaması:

void SpawnCreature(creatureType)
{
    switch(creatureType)
    {
         case Orc:

           Orc anOrc = new Orc();
           anORc.Walk();

          case Troll:

            Troll aTroll = new Troll();
             aTroll.Walk();
    }
}

Ve bunun nasıl dağınık hale geldiğini görebilirsiniz. Burada bir arabirim kullanabilirsiniz, böylece ön ucunuz bir kez yazılır ve (burada önemli olanı) test edilir ve daha sonra gerektiğinde daha fazla arka uç öğesi ekleyebilirsiniz:

interface ICreature
{
    void Walk(int distance)
}

public class Troll : ICreature
public class Orc : ICreature 

//etc

Ön uç o zaman:

void SpawnCreature(creatureType)
{
    ICreature creature;

    switch(creatureType)
    {
         case Orc:

           creature = new Orc();

          case Troll:

            creature = new Troll();
    }

    creature.Walk();
}

Ön uç şimdi sadece ICreature arayüzünü önemsiyor - bir trolün veya bir orkın dahili uygulamasıyla ilgili değil, sadece ICreature'ı uyguladıkları gerçeğiyle ilgili.

Buna bu açıdan bakıldığında dikkat edilmesi gereken önemli bir nokta, soyut bir yaratık sınıfını da kolayca kullanabilmeniz ve bu açıdan da aynı etkiye sahip olmasıdır.

Ve yaratılışı bir fabrikaya çıkarabilirsiniz:

public class CreatureFactory {

 public ICreature GetCreature(creatureType)
 {
    ICreature creature;

    switch(creatureType)
    {
         case Orc:

           creature = new Orc();

          case Troll:

            creature = new Troll();
    }

    return creature;
  }
}

Ve sonra ön ucumuz şöyle olur:

CreatureFactory _factory;

void SpawnCreature(creatureType)
{
    ICreature creature = _factory.GetCreature(creatureType);

    creature.Walk();
}

Ön uç artık Troll ve Orc'un uygulandığı kütüphaneye bir referansa sahip olmak zorunda bile değil (fabrikanın ayrı bir kütüphanede olması koşuluyla) - onlar hakkında hiçbir şey bilmesine gerek yok.

B: Aksi takdirde homojen veri yapınızda yalnızca bazı canlıların sahip olacağı işlevselliğe sahip olduğunuzu varsayalım , ör.

interface ICanTurnToStone
{
   void TurnToStone();
}

public class Troll: ICreature, ICanTurnToStone

Ön uç şu şekilde olabilir:

void SpawnCreatureInSunlight(creatureType)
{
    ICreature creature;

    switch(creatureType)
    {
         case Orc:

           creature = new Orc();

          case Troll:

            creature = new Troll();
    }

    creature.Walk();

    if (creature is ICanTurnToStone)
    {
       (ICanTurnToStone)creature.TurnToStone();
    }
}

C: Bağımlılık enjeksiyonu için kullanım

Bağımlılık enjeksiyon çerçevelerinin çoğu, ön uç kodu ile arka uç uygulaması arasında çok gevşek bir bağlantı olduğunda çalışmak daha kolaydır. Yukarıdaki fabrika örneğimizi alırsak ve fabrikamızın bir arayüz uygulamasını sağlarsak:

public interface ICreatureFactory {
     ICreature GetCreature(string creatureType);
}

Ön ucumuz, yapıcı (tipik olarak) aracılığıyla bu enjekte edilebilir (örn., Bir MVC API denetleyicisi):

public class CreatureController : Controller {

   private readonly ICreatureFactory _factory;

   public CreatureController(ICreatureFactory factory) {
     _factory = factory;
   }

   public HttpResponseMessage TurnToStone(string creatureType) {

       ICreature creature = _factory.GetCreature(creatureType);

       creature.TurnToStone();

       return Request.CreateResponse(HttpStatusCode.OK);
   }
}

DI çerçevemizle (örneğin Ninject veya Autofac), bunları bir kurucuda bir ICreatureFactory gerektiğinde CreatureFactory'nin bir örneğinin oluşturulacağı şekilde ayarlayabiliriz - bu kodumuzu güzel ve basit hale getirir.

Ayrıca, denetleyicimiz için birim testi yazdığımızda, sahte bir ICreatureFactory sağlayabiliriz (örneğin, somut uygulama DB erişimi gerektiriyorsa, birim testlerimizin buna bağlı olmasını istemiyoruz) ve denetleyicimizdeki kodu kolayca test edebiliriz .

D: Örneğin, 'eski' nedenlerden dolayı iyi yapılandırılmamış iki A ve B projeniz var ve A'nın B'ye referansı var.

Daha sonra B'de zaten A'da bir yöntem çağırması gereken işlevsellik bulursunuz. Dairesel bir başvuru alırken somut uygulamaları kullanarak bunu yapamazsınız.

B'de, A'daki sınıfın uyguladığı bir arabirim olabilir. B'deki yönteminiz, somut nesne A'da bir tür olmasına rağmen, arabirimi sorunsuz bir şekilde uygulayan bir sınıfın örneğinden geçirilebilir.


6
Ne olduğunuzu söylemeye çalışan bir cevap bulduğunuzda can sıkıcı değil mi, ama çok daha iyi yapıyor - stackoverflow.com/a/93998/117215
Paddy

18
İyi haber şu an ölü bir sayfa ve seninki :) değil. İyi örnek!
Sealer_05

1
C tartışmanız beni biraz kaybetti. Ancak, A ve B tartışmalarınızı seviyorum çünkü ikisi de temel olarak arayüzlerin birden fazla sınıfta ortak işlevsellik sağlamak için nasıl kullanılabileceğini açıklıyor. Benim için hala bulanık olan arayüz alanı, arayüzlerin daha gevşek kuplaj için nasıl kullanıldığıdır. Belki de C tartışmanızın konusu budur? Eğer öyleyse, daha ayrıntılı bir örneğe ihtiyacım var sanırım :)
user2075599

1
Örneğinizde bana göre ICreature, Troll ve Orc için soyut bir temel sınıf (Yaratık?) Olmaya daha uygun olacaktır. O zaman yaratıklar arasındaki herhangi bir ortak mantık orada uygulanabilir. Yani ICreature arayüzüne hiç ihtiyacınız yok ...
Skarsnik

1
@Skarsnik - oldukça, bu notun anlamı şudur: "Buna bu açıdan bakarken dikkat edilmesi gereken önemli bir nokta, soyut bir yaratık sınıfını da kolayca kullanabilmenizdir ve bu bakış açısından, bu aynıdır. etki."
Paddy

33

Aşağıda açıkladığınız örnekler verilmiştir:

public interface IFood // not Pizza
{
    public void Prepare();

}

public class Pizza : IFood
{
    public void Prepare() // Not order for explanations sake
    {
        //Prepare Pizza
    }
}

public class Burger : IFood
{
    public void Prepare()
    {
        //Prepare Burger
    }
}

27

Yukarıdaki örnekler pek mantıklı değil. Yukarıdaki örnekleri sınıfları kullanarak gerçekleştirebilirsiniz (yalnızca bir sözleşme olarak davranmasını istiyorsanız soyut sınıf ):

public abstract class Food {
    public abstract void Prepare();
}

public class Pizza : Food  {
    public override void Prepare() { /* Prepare pizza */ }
}

public class Burger : Food  {
    public override void Prepare() { /* Prepare Burger */ }
}

Arayüz ile aynı davranışı elde edersiniz. List<Food>Hangi sınıfın üstte oturduğunu bilerek bir ve yineleyebilirsiniz.

Daha yeterli örnek çoklu kalıtım olacaktır:

public abstract class MenuItem {
    public string Name { get; set; }
    public abstract void BringToTable();
}

// Notice Soda only inherits from MenuItem
public class Soda : MenuItem {
    public override void BringToTable() { /* Bring soda to table */ }
}


// All food needs to be cooked (real food) so we add this
// feature to all food menu items
public interface IFood {
    void Cook();
}

public class Pizza : MenuItem, IFood {
    public override void BringToTable() { /* Bring pizza to table */ }
    public void Cook() { /* Cook Pizza */ }
}

public class Burger : MenuItem, IFood {
    public override void BringToTable() { /* Bring burger to table */ }
    public void Cook() { /* Cook Burger */ }
}

Sonra hepsini olarak kullanabilirsiniz MenuItemve her yöntem çağrısını nasıl ele aldıkları umurumda değil.

public class Waiter {
    public void TakeOrder(IEnumerable<MenuItem> order) 
    {
        // Cook first
        // (all except soda because soda is not IFood)
        foreach (var food in order.OfType<IFood>())
            food.Cook();

        // Bring them all to the table
        // (everything, including soda, pizza and burger because they're all menu items)
        foreach (var menuItem in order)
            menuItem.BringToTable();
    }
}

2
"Neden sadece arabirim yerine soyut bir sınıf kullanmıyorsunuz?" Sorusuna cevap veren bir cevap bulmak için buraya kadar ilerlemem gerekiyordu. Görünüşe göre gerçekten sadece c # 'ın çoklu kalıtım eksikliğini ele almak.
SyntaxRules

2
Evet, bu benim için en iyi açıklama. Arayüzün neden soyut bir sınıftan daha yararlı olabileceğini açıkça açıklayan tek kişi. (yani: bir pizza bir MenuItem ve aynı zamanda Gıda iken soyut bir sınıfla sadece bir ya da başka olabilir ama her ikisi de olamaz)
Maxter

25

Analoji ile Basit Açıklama

Çözülmesi Gereken Sorun: Polimorfizmin amacı nedir?

Benzetme: Yani bir şantiyede bir öncüyüm.

Esnaf her zaman şantiyede yürür. O kapılardan kimin geçeceğini bilmiyorum. Ama temelde onlara ne yapmaları gerektiğini söylüyorum.

  1. Bir marangozsa diyorum ki: ahşap iskele inşa et.
  2. Eğer bir tesisatçı ise, "Boruları kur" diyorum.
  3. Eğer bir elektrikçi ise, "Kabloları çıkarın ve fiber optik kablolarla değiştirin" diyorum.

Yukarıdaki yaklaşımla ilgili sorun şu ki: (i) o kapıda kimin yürüdüğünü bilmek ve kim olduğuna bağlı olarak, onlara ne yapmaları gerektiğini söylemeliyim. Bu, belirli bir ticaretle ilgili her şeyi bilmek zorunda olduğum anlamına geliyor. Bu yaklaşımla ilişkili maliyetler / faydalar vardır:

Ne yapılacağını bilmenin sonuçları:

  • Marangozun kod değişir, bu araçlar: BuildScaffolding()için BuildScaffold()o zaman ben de çağıran sınıf (yani değiştirmek zorunda kalacak (yani hafif bir isim değişikliği) Forepersonyanı sınıfı) - Yaptığınız gerekecek iki temelde (kod değişiklikleri yerine ) sadece bir tane. Polimorfizm ile (temelde) aynı sonucu elde etmek için sadece bir değişiklik yapmanız gerekir.

  • İkincisi, sürekli olarak sormak zorunda kalmayacaksınız: Kimsiniz? tamam bunu yap ... sen kimsin? tamam bunu ..... polimorfizm - bu KURU DRYs ve bazı durumlarda çok etkilidir:

  • polimorfizm ile mevcut kodları değiştirmeden kolayca esnaf sınıfları ekleyebilirsiniz. (yani SOLID tasarım ilkelerinin ikincisi: Açma-kapama ilkesi).

Çözüm

Kapıda kimin yürüdüğüne bakılmaksızın, "İş ()" diyebileceğim ve saygı duydukları işleri yaptıkları bir senaryo düşünün: tesisatçı borularla ve elektrikçinin tellerle uğraşacağı.

Bu yaklaşımın yararı şu şekildedir: (i) o kapıdan kimin yürüdüğünü tam olarak bilmeme gerek yok - bilmem gereken tek şey bir tür tradie olacakları ve çalışabilecekleri ve ikincisi (ii) Söz konusu ticaret hakkında hiçbir şey bilmeme gerek yok. Tradie bununla ilgilenecek.

Bunun yerine:

If(electrician) then  electrician.FixCablesAndElectricity() 

if(plumber) then plumber.IncreaseWaterPressureAndFixLeaks() 

Böyle bir şey yapabilirim:

ITradesman tradie = Tradesman.Factory(); // in reality i know it's a plumber, but in the real world you won't know who's on the other side of the tradie assignment.

tradie.Work(); // and then tradie will do the work of a plumber, or electrician etc. depending on what type of tradesman he is. The foreman doesn't need to know anything, apart from telling the anonymous tradie to get to Work()!!

Avantajı nedir?

Faydası, marangozun vb.Özel iş gereksinimleri değişirse, ön karakolun kodunu değiştirmesi gerekmeyecektir - bilmesine veya bakım yapmasına gerek yoktur. Önemli olan tek şey marangozun Work () ile ne kastedildiğini bilmesidir. İkincisi, şantiyeye yeni bir tip inşaat işçisi gelirse, ustabaşı ticaret hakkında hiçbir şey bilmeye gerek duymaz - tüm ustabaşı, inşaat işçisi (.eg Welder, Glazier, Kiremit vb.) biraz İş () yap.


Resimli Problem ve Çözümü (Arayüzlü ve Arayüzsüz):

Arabirim yok (Örnek 1):

Örnek 1: arabirimsiz

Arabirim yok (Örnek 2):

Örnek 2: arabirimsiz

Bir arayüz ile:

Örnek 3: Arayüz Kullanmanın Yararları

özet

Bir arayüz, tam olarak kim olduklarını veya yapabileceklerinin özelliklerini bilmenize gerek kalmadan, kişiye atanan işi yapmasını sağlar. Bu, mevcut kodunuzu değiştirmeden kolayca yeni türler (ticaret) eklemenize izin verir (teknik olarak küçük bir biti değiştirirsiniz) ve bu daha işlevsel bir programlama metodolojisine karşı bir OOP yaklaşımının gerçek yararıdır.

Yukarıdakilerden herhangi birini anlamıyorsanız veya açık değilse bir yorumda sorun ve cevabı daha iyi yapmaya çalışacağım.


4
Artı bir kapüşonlu olarak işe başlayan adam için bir tane.
Yusha

11

Python'da kullanabileceğiniz gibi ördek yazmanın yokluğunda , C # soyutlamalar sağlamak için arayüzlere dayanır. Bir sınıfın bağımlılıklarının tümü somut türdeyse, başka bir türden geçemezdiniz - arabirimleri kullanarak arabirimi uygulayan herhangi bir türde geçirebilirsiniz.


3
+1 Doğru, ördek yazmanız varsa, arayüzlere ihtiyacınız yok. Ancak daha güçlü tip güvenliği sağlarlar.
Groo

11

Pizza örneği kötü çünkü siparişi işleyen soyut bir sınıf kullanmalısınız ve pizzalar sadece pizza türünü geçersiz kılmalıdır.

Paylaşılan bir özelliğiniz olduğunda arabirimler kullanırsınız, ancak sınıflarınız farklı yerlerden miras alır veya kullanabileceğiniz ortak bir kodunuz yoksa. Örneğin, bu bertaraf edilebilecek şeyler kullanılırIDisposable , edileceğini biliyorsunuz, sadece bertaraf edildiğinde ne olacağını bilmiyorsunuz.

Arabirim, yalnızca bir nesnenin yapabileceği bazı şeyleri, hangi parametreleri ve hangi dönüş türlerini bekleyeceğini söyleyen bir sözleşmedir.


10

Temel sınıfları denetlemediğiniz veya sahip olmadığınız durumu düşünün.

Örneğin, Winforms için .NET'te, tümü .NET çerçevesinde tamamen tanımlanmış olan temel sınıf Denetimi'nden devralınan görsel denetimler alın.

Özel denetimler oluşturma işinde olduğunuzu varsayalım. Yeni düğmeler, metin kutuları, liste görünümleri, ızgaralar oluşturmak istersiniz ve hepsinin kontrol grubunuza özgü belirli özelliklere sahip olmasını istersiniz.

Örneğin, temayı işlemek için ortak bir yol veya yerelleştirmeyi işlemek için ortak bir yol isteyebilirsiniz.

Bu durumda "sadece bir temel sınıf oluşturamazsınız" çünkü bunu yaparsanız, kontrollerle ilgili her şeyi yeniden uygulamanız gerekir .

Bunun yerine, Button, TextBox, ListView, GridView, vs.'den iner ve kodunuzu eklersiniz.

Ancak bu bir sorun teşkil ediyor, şimdi hangi kontrollerin "sizin" olduğunu nasıl belirtebilirsiniz, "formdaki tüm denetimler için temayı X olarak ayarlayın" yazan bir kodu nasıl oluşturabilirsiniz?

Arabirim girin.

Arayüzler, bir nesneye bakmanın, nesnenin belirli bir sözleşmeye uyduğunu belirleme yoludur.

"YourButton" u yaratacak, Button'dan inecek ve ihtiyacınız olan tüm arayüzler için destek ekleyeceksiniz.

Bu, aşağıdaki gibi bir kod yazmanıza izin verir:

foreach (Control ctrl in Controls)
{
    if (ctrl is IMyThemableControl)
        ((IMyThemableControl)ctrl).SetTheme(newTheme);
}

Bu, arayüzler olmadan mümkün olmaz, bunun yerine böyle bir kod yazmanız gerekir:

foreach (Control ctrl in Controls)
{
    if (ctrl is MyThemableButton)
        ((MyThemableButton)ctrl).SetTheme(newTheme);
    else if (ctrl is MyThemableTextBox)
        ((MyThemableTextBox)ctrl).SetTheme(newTheme);
    else if (ctrl is MyThemableGridView)
        ((MyThemableGridView)ctrl).SetTheme(newTheme);
    else ....
}

Evet, biliyorum, "is" kullanmamalısınız ve sonra dökün, bir kenara bırakın.
Lasse V. Karlsen

4
iç çeker biliyorum, ama bu tesadüfi.
Lasse V. Karlsen

7

Bu durumda, sadece bir Pizza temel sınıfı tanımlayabilir ve onlardan miras alabilirsiniz. Ancak, Arayüzlerin başka yollarla elde edilemeyen şeyleri yapmanıza izin vermesinin iki nedeni vardır:

  1. Bir sınıf birden fazla arabirim uygulayabilir. Sadece sınıfın sahip olması gereken özellikleri tanımlar. Bir dizi aralığın uygulanması, bir sınıfın farklı yerlerde birden fazla işlevi yerine getirebileceği anlamına gelir.

  2. Bir arabirim sınıftan veya arayandan daha zor bir kapsamda tanımlanabilir. Bu, işlevselliği ayırabileceğiniz, proje bağımlılığını ayırabileceğiniz ve işlevselliği bir proje veya sınıfta tutabileceğiniz ve bunun başka bir yerde uygulanabileceği anlamına gelir.

2'nin bir sonucu olarak, kullanılan arabirimi değiştirebilmeniz, yalnızca uygun arabirimi uygulamasını gerektirmesidir.


6

C # 'da birden fazla devralma kullanamayacağınızı düşünün ve ardından sorunuza tekrar bakın.



4

Şekiller çizmek için bir API üzerinde çalışıyorsanız, DirectX veya grafik çağrıları veya OpenGL kullanmak isteyebilirim. Böylece, benim uyguladığım uygulamadan soyutlayacağım bir arayüz oluşturacağım.

Bu yüzden bir fabrika yöntemi çağırırsınız MyInterface i = MyGraphics.getInstance(). Daha sonra, bir sözleşmeniz var, böylece hangi işlevlerde bekleyeceğinizi biliyorsunuz MyInterface. Böylece, i.drawRectangleveyai.drawCube bir kütüphaneyi diğeri için değiştirirseniz, işlevlerin desteklendiğini bilirsiniz.

Bağımlılık Enjeksiyonu kullanıyorsanız bu daha önemli hale gelir, çünkü bir XML dosyasındaki uygulamaları takas edebilirsiniz.

Yani, genel kullanım için dışa aktarılabilen bir kripto kütüphaneniz ve sadece Amerikan şirketlerine satılık olan bir kripto kütüphaneniz olabilir ve fark, bir yapılandırma dosyasını değiştirmenizdir ve programın geri kalanı değişti.

Bu, örneğin Listdeğişkenleri kullanmanız ve bir ArrayList veya LinkedList olup olmadığını düşünmemeniz gerektiğinden, .NET'teki koleksiyonlarla çok kullanılır .

Arabirimi kodladığınız sürece, geliştirici gerçek uygulamayı değiştirebilir ve programın geri kalanı değişmeden kalır.

Bu, tüm arayüzleri alay edebileceğiniz için birim testi yaparken de kullanışlıdır, bu yüzden bir veritabanına gitmek zorunda değilim, ancak statik verileri döndüren sahte bir uygulamaya gitmem, böylece endişelenmeden yöntemimi test edebilirim veritabanı bakım için kapalı veya değil.


4

Bir arayüz, gerçekte uygulayıcı sınıfların izlemesi gereken bir sözleşmedir, aslında bildiğim hemen hemen her tasarım modelinin temelidir.

Örneğinizde, arayüz oluşturulur, çünkü daha sonra Pizza arayüzünü uygulayan IS A Pizza olan her şeyin

public void Order();

Bahsedilen koddan sonra böyle bir şeye sahip olabilirsiniz:

public void orderMyPizza(IPizza myPizza) {
//This will always work, because everyone MUST implement order
      myPizza.order();
}

Bu şekilde polimorfizm kullanıyorsunuz ve tek dikkat ettiğiniz şey nesnelerinizin order () öğesine yanıt vermesidir.


4

Bu sayfada "kompozisyon" kelimesini aradım ve bir kez görmedim. Bu cevap yukarıda bahsedilen cevaplara ek olarak çok fazladır.

Nesneye Dayalı Bir Projede arabirimleri kullanmanın kesinlikle önemli nedenlerinden biri, kalıtım yerine kompozisyonu tercih etmenize izin vermesidir. Arabirimler uygulayarak, uygulamalarınızı bunlara uyguladığınız çeşitli algoritmalardan ayırabilirsiniz.

Derek Banas'ın bu harika "Dekoratör Deseni" öğreticisi (ki - yeterince komik - pizzayı örnek olarak kullanıyor) değerli bir örnek:

https://www.youtube.com/watch?v=j40kRwSm4VE


2
Bunun en iyi cevap olmadığı için gerçekten şok oldum. Arayüzleri tüm kullanışlılıklarında, kompozisyonda en çok kullanılanlardır.
Maciej Sitko

3

Arabirimler farklı sınıflar arasında bağlantı uygulamak içindir. örneğin, araba ve bir ağaç sınıfınız var;

public class Car { ... }

public class Tree { ... }

her iki sınıf için de yazılabilir bir işlevsellik eklemek istiyorsunuz. Ancak her sınıfın kendi yanma yolları vardır. yani basitçe yaparsınız;

public class Car : IBurnable
{
public void Burn() { ... }
}

public class Tree : IBurnable
{
public void Burn() { ... }
}

2
Böyle örneklerde beni rahatsız eden soru şudur: Bu neden yardımcı oluyor? Şimdi IBurnable türü argümanını bir yönteme geçirebilir miyim ve IBurnable arabirimine sahip tüm sınıflar işlenebilir mi? Bulduğum Pizza örneğiyle aynı. Bunu yapabilmeniz güzel, ama faydasını görmüyorum. Örneği uzatabilir misiniz (çünkü şu anda gerçekten kalınım) ya da onunla çalışmayacak bir örnek verebilir misiniz (yine, şu anda gerçekten kalın hissediyorum). Çok teşekkürler.
Nebelhom

1
Katılıyorum. Interface = "Yapabilir". Sınıf / Soyut Calss = "Is A"
Peter.Wang

3

Arayüzler alacaksınız, ihtiyacınız olduğunda :) Örnekler üzerinde çalışabilirsiniz, fakat Aha! etkisi gerçekten onları elde etmek için.

Artık arayüzlerin ne olduğunu biliyorsunuz, sadece onlarsız kodlayın. Er ya da geç, arayüzlerin kullanımının en doğal şey olacağı bir problemle karşılaşacaksınız.


3

Çok fazla gönderinin bir arayüzün en önemli nedenini içermemesine şaşırdım: Tasarım Desenleri . Kontratları kullanmanın daha büyük resmi ve makine kodunun sözdizimi dekorasyonu olmasına rağmen (dürüst olmak gerekirse, derleyici muhtemelen onları görmezden geliyor), soyutlama ve arayüzler OOP, insan anlayışı ve karmaşık sistem mimarileri için çok önemlidir.

Tam bir kızak 3 yemek için pizza benzetmesini genişletelim. Prepare()Tüm gıda kategorilerimiz için hala temel arayüzümüz olacak, ancak kurs seçimleri (başlangıç, ana, tatlı) ve gıda türleri (tuzlu / tatlı, vejetaryen / vejetaryen olmayan, glutensiz vb.).

Bu spesifikasyonlara dayanarak , tüm süreci kavramsallaştırmak için Soyut Fabrika modelini uygulayabiliriz , ancak sadece temellerin somut olmasını sağlamak için arayüzler kullanabiliriz. Diğer her şey esnek hale gelebilir veya polimorfizmi teşvik edebilir, ancak Coursebu uygulamanın farklı sınıfları arasında kapsüllemeyi koruyabilir .ICourse arabirimi .

Daha fazla zamanım olsaydı, bunun tam bir örneğini çizmek isterdim, ya da birisi bunu benim için uzatabilir, ancak özet olarak, bir C # arayüzü bu tür bir sistemin tasarımında en iyi araç olurdu.


1
Bu cevap daha fazla puanı hak ediyor! Durum Paternleri gibi tasarlanmış desenlerde kullanıldığında arayüzler parlıyor. Daha fazla bilgi için plus.google.com/+ZoranHorvat-Programming konusuna bakın
Alex Nolasco

3

Dikdörtgen şekilli nesneler için bir arayüz:

interface IRectangular
{
    Int32 Width();
    Int32 Height();
}

Tüm talep ettiği, nesnenin genişliğine ve yüksekliğine erişmek için yollar uygulamanızdır.

Şimdi herhangi bir nesne üzerinde çalışacak bir yöntem tanımlayalım IRectangular:

static class Utils
{
    public static Int32 Area(IRectangular rect)
    {
        return rect.Width() * rect.Height();
    }
}

Bu herhangi bir dikdörtgen nesnenin alanını döndürür.

SwimmingPoolDikdörtgen olan bir sınıfı uygulayalım :

class SwimmingPool : IRectangular
{
    int width;
    int height;

    public SwimmingPool(int w, int h)
    { width = w; height = h; }

    public int Width() { return width; }
    public int Height() { return height; }
}

Ve Houseayrıca dikdörtgen olan başka bir sınıf :

class House : IRectangular
{
    int width;
    int height;

    public House(int w, int h)
    { width = w; height = h; }

    public int Width() { return width; }
    public int Height() { return height; }
}

Bununla birlikte, Areayöntemi evlerde veya yüzme havuzlarında çağırabilirsiniz :

var house = new House(2, 3);

var pool = new SwimmingPool(3, 4);

Console.WriteLine(Utils.Area(house));
Console.WriteLine(Utils.Area(pool));

Bu şekilde, sınıflarınız herhangi bir sayıda arabirimden davranışı (statik yöntemler) devralabilir.


3

Bir arabirim, belirli bir işlevselliğin sağlayıcısı ve karşılık gelen tüketiciler arasında bir sözleşme tanımlar. Uygulamayı sözleşmeden ayırır (arayüz). Nesneye yönelik mimariye ve tasarıma bir göz atmalısınız. Wikipedia ile başlamak isteyebilirsiniz: http://en.wikipedia.org/wiki/Interface_(computing)


3

Ne ?

Arayüzler temel olarak tüm sınıfların uyguladığı bir sözleşmedir , Arayüzü izlemesi gereken . Bir sınıfa benziyorlar ama uygulamaları yok.

In C#Arayüz Kongre tarafından isimleri bir arayüz olarak adlandırılan şekiller yapmak istiyorsanız bir 'ben' öyleyse, olarak ilan ediyorum önüne doğrudan doğruya ile tanımlanırIShapes

Şimdi Neden?

Improves code re-usability

Çizmek istiyorsunuz diyelim Circle, Triangle. sen grup can onları birlikte ve onları aramak Shapesve çizmek yöntemleri var Circleve Triangle yarın daha 2 yaşamaya karar verebilir çünkü beton uygulanmasını sahip olacağını kötü bir fikir Shapes Rectangle& Square. Şimdi bunları eklediğinizde, kodunuzun diğer bölümlerini kırarsınız.

Arabirim ile farklı uygulamayı Sözleşmeden ayırırsınız


Canlı Senaryo 1. Gün

Daire ve Üçgen Çizmek için bir Uygulama oluşturmanız istendi

interface IShapes
{
   void DrawShape();
   
 }

class Circle : IShapes
{
    
    public void DrawShape()
    {
        Console.WriteLine("Implementation to Draw a Circle");
    }
}

Class Triangle: IShapes
{
     public void DrawShape()
    {
        Console.WriteLine("Implementation to draw a Triangle");
    }
}
static void Main()
{
     List <IShapes> shapes = new List<IShapes>();
        shapes.Add(new Circle());
        shapes.Add(new Triangle());

        foreach(var shape in shapes)
        {
            shape.DrawShape();
        }
}

Canlı Senaryo 2. Gün

Eklentiyi istense Squareve Rectanglebuna yapmanız gereken tek şey bunun için implentation oluşturmaktır class Square: IShapesve Mainlisteye eklemeshapes.Add(new Square());


Neden yüzlerce kez oylanan düzinelerce başka cevapla 6 yaşındaki bir soruya cevap ekliyorsunuz? Burada henüz söylenmemiş bir şey yok.
Jonathon Reinhart

@JonathonReinhart, evet, ben de öyle düşünmüştüm, ama sonra bu örneği düşünüyordum ve açıklanmasının bazı bedenle ilgili olarak diğerlerinden daha iyi yardımcı olacağını düşünüyorum.
Clint

2

Burada çok iyi cevaplar var ama biraz farklı bir bakış açısıyla denemek istiyorum.

Nesneye yönelik tasarımın SOLID ilkelerini biliyor olabilirsiniz. Özetle:

S - Tek Sorumluluk İlke O - Açık / Kapalı İlke L - Liskov İkame İlkesi I - Arayüz Ayrışma İlkesi D - Bağımlılık Ters Çevirme İlkesi

SOLID ilkelerine uymak, temiz, iyi faktörlü, uyumlu ve gevşek bir şekilde birleştirilmiş kod üretilmesine yardımcı olur. Verilen:

"Bağımlılık yönetimi, yazılımın her ölçeğinde en büyük zorluktur" (Donald Knuth)

bağımlılık yönetimine yardımcı olan her şey büyük bir kazançtır. Arayüzler ve Bağımlılık Ters Çevirme Prensibi, kodu somut sınıflara bağımlılıklardan ayırmaya gerçekten yardımcı olur, bu nedenle kod davranışlar açısından yazılabilir ve gerekçelendirilebilir uygulamalardan ziyade . Bu, kodun derleme zamanı yerine çalışma zamanında oluşturulabilecek bileşenlere bölünmesine yardımcı olur ve ayrıca bu bileşenlerin, kodun geri kalanını değiştirmek zorunda kalmadan kolayca takılabilir ve çıkarılabileceği anlamına gelir.

Arayüzler özellikle kodun bir servisler topluluğuna entegre edilebildiği Bağımlılık Ters Çevirme Prensibi ile yardımcı olur ve her servis bir arayüz tarafından tanımlanır. Hizmetler daha sonra bir yapıcı parametresi olarak geçirilerek çalışma zamanında sınıflara "enjekte edilebilir". Birim testleri yazmaya başlar ve test odaklı geliştirme kullanırsanız bu teknik gerçekten kritik hale gelir. Dene! Arayüzlerin kodu ayrı ayrı ayrı ayrı test edilebilen yönetilebilir parçalara ayırmaya nasıl yardımcı olduğunu hızlı bir şekilde anlayacaksınız.


1

Arayüzlerin temel amacı, kodunuzu ayrıştırılmış ve genişletilebilirliğe izin veren bu arayüzü uygulayan diğer sınıflarla aranızda sözleşme yapmasıdır.


1

Onlar gerçekten harika örnekler soruyorlar.

Bir diğeri, bir switch deyimi durumunda, rio'nun belirli bir şekilde her görevi yerine getirmesini istediğinizde artık bakım yapmanız ve geçiş yapmanız gerekmez.

Pizza örneğinizde, bir pizza yapmak istiyorsanız, arayüz ihtiyacınız olan her şeydir, oradan her pizza kendi mantığına bakar.

Bu, kuplajı ve siklomatik karmaşıklığı azaltmaya yardımcı olur. Hala mantığı uygulamalısınız, ancak daha geniş resimde takip etmeniz gereken daha az şey olacaktır.

Daha sonra her pizza için o pizzaya özgü bilgileri takip edebilirsiniz. Diğer pizzaların önemi yok çünkü sadece diğer pizzaların bilmesi gerekiyor.


1

Arayüzler üzerinde düşünmenin en basit yolu, kalıtımın ne anlama geldiğini tanımaktır. CC sınıfı C sınıfını miras alırsa, her ikisi de:

  1. CC Sınıfı, C sınıfının herkese açık veya korunan üyelerini kendilerine aitmiş gibi kullanabilir ve bu nedenle yalnızca ana sınıfta olmayan şeyleri uygulaması gerekir.
  2. Bir CC'ye yapılan bir referans, C'ye bir referans bekleyen bir rutine veya değişkene aktarılabilir veya atanabilir.

Bu iki miras işlevi bir anlamda bağımsızdır; kalıtım her ikisini aynı anda uygulasa da, ikincisini birincisi olmadan uygulamak da mümkündür. Bu yararlıdır, çünkü bir nesnenin iki veya daha fazla ilişkisiz sınıftan üyeleri miras almasına izin vermek, bir tür şeyin birden çok tür için ikame edilmesine izin vermekten çok daha karmaşıktır.

Bir arabirim soyut bir temel sınıfa benzer, ancak anahtar bir farkla: bir temel sınıfı devralan bir nesne başka bir sınıfı devralamaz. Aksine, bir nesne istenen herhangi bir sınıfı devralma yeteneğini etkilemeden veya diğer arayüzleri uygulama yeteneğini etkilemeden bir arayüz uygulayabilir.

Bunun güzel bir özelliği (.net çerçevesinde, IMHO altında), bir nesnenin yapabileceği şeyleri bildirerek belirtmeyi mümkün kılmasıdır. Örneğin bazı nesneler, şeyleri dizine göre (bir Liste ile mümkün olduğu şekilde) alabildikleri veri kaynağı nesnesini isteyecektir, ancak orada hiçbir şey depolamaları gerekmez. Diğer rutinler, şeyleri indeksle (Collection.Add'da olduğu gibi) depolayamayacakları bir veri saklama nesnesine ihtiyaç duyacaklar, ancak hiçbir şeyi tekrar okumaları gerekmeyecek. Bazı veri türleri dizine erişime izin verir, ancak yazmaya izin vermez; diğerleri yazmaya izin verir, ancak dizine erişime izin vermez. Bazıları elbette her ikisine de izin verecek.

ReadableByIndex ve Appendable alakasız temel sınıflar olsaydı, hem ReadableByIndex'i bekleyen hem de Appendable'ı bekleyen şeylere geçirilebilecek bir tür tanımlamak imkansız olurdu. Biri ReadableByIndex veya Appendable'ın diğerinden türetilmesini sağlayarak bunu hafifletmeye çalışabilir; türetilmiş sınıfın her iki amaç için de halka açık üyeler bulundurması gerekir, ancak bazı halka açık üyelerin aslında çalışmayabileceği konusunda uyarırlar. Microsoft'un bazı sınıfları ve arayüzleri bunu yapar, ancak bu oldukça zordur. Daha temiz bir yaklaşım, farklı amaçlar için arayüzlere sahip olmak ve daha sonra nesnelerin gerçekte yapabilecekleri şeyler için arayüzler uygulamaktır. Birinin arayüzü IReadableByIndex ve başka bir arayüzü IAppendable varsa,


1

Arayüzler, başka bir arayüz oluşturmak için papatya dizimine de bağlanabilir. Birden çok Arayüz uygulama yeteneği geliştiriciye mevcut sınıf işlevselliğini değiştirmek zorunda kalmadan sınıflarına işlevsellik ekleme avantajı sağlar (SOLID İlkeleri)

O = "Sınıflar uzatma için açık ancak değişiklik için kapalı olmalıdır"


1

Benim için bir arayüzün bir avantajı / yararı, soyut bir sınıftan daha esnek olmasıdır. Yalnızca 1 soyut sınıfı miras alabileceğiniz, ancak birden fazla arabirim uygulayabildiğiniz için, birçok yerde soyut bir sınıfı devralan bir sistemde yapılan değişiklikler sorunlu hale gelir. 100 yerde miras alınırsa, bir değişiklik 100 değerinin hepsinde değişiklik yapılmasını gerektirir. Ancak, arayüzle yeni değişikliği yeni bir arayüze yerleştirebilir ve bu arayüzü yalnızca gerektiği yerde kullanabilirsiniz (SOLID'den Arayüz Sırası). Ayrıca, arabirim örneğindeki bir nesne, arabirimi kaç yerde uyguladığına rağmen bellekte sadece bir kez kullanıldığından, bellek kullanımı arabirimle daha az olacağı gibi görünüyor.


1

Arayüzler, sıkı bir şekilde birleştirilen soyut sınıfa farklı kılan, gevşek bir şekilde bağlanmış bir şekilde tutarlılığı sağlamak için kullanılır.Bu nedenle, genellikle bir sözleşme olarak da tanımlanır. arayüz tarafından tanımlanır ve içinde somut bir unsur yoktur.

Aşağıdaki grafik tarafından desteklenen bir örnek vereceğim.

Bir fabrikada 3 tip makine olduğunu hayal edin. Bir dikdörtgen makinesi, bir üçgen makinesi ve bir çokgen makinesi.Zamanlar rekabetçi ve operatör eğitimini kolaylaştırmak istiyorsunuz. yeşil bir başlat düğmesi ve kırmızı durdurma düğmesi var. şimdi 3 farklı makinede 3 farklı tür makineyi başlatmak ve durdurmak için tutarlı bir yolunuz var. bu sınıflar arasında çok farklı olabilecek tutarlılığı sağlayacak mı? Arayüz cevaptır.

resim açıklamasını buraya girin

Görselleştirmenize yardımcı olacak basit bir örnek, soyut sınıfı neden kullanmadığınızı sorabilirsiniz. Bir arabirim ile nesnelerin doğrudan ilişkili veya devralınması gerekmez ve yine de farklı sınıflar arasında tutarlılık sağlayabilirsiniz.

public interface IMachine
{
    bool Start();
    bool Stop();
}

public class Car : IMachine
{
    public bool Start()
    {
        Console.WriteLine("Car started");
        return true;
    }

    public bool Stop()
    {
        Console.WriteLine("Car stopped");
        return false;
    }
}

public class Tank : IMachine
{
    public bool Start()
    {
        Console.WriteLine("Tank started");
        return true;
    }

    public bool Stop()
    {
        Console.WriteLine("Tank stopped");
        return false;
    }
}

class Program
{
    static void Main(string[] args)
    {
        var car = new Car();
        car.Start();
        car.Stop();

        var tank = new Tank();
        tank.Start();
        tank.Stop();

    }
}

1
class Program {
    static void Main(string[] args) {
        IMachine machine = new Machine();
        machine.Run();
        Console.ReadKey();
    }

}

class Machine : IMachine {
    private void Run() {
        Console.WriteLine("Running...");
    }
    void IMachine.Run() => Run();
}

interface IMachine
{
    void Run();
}

Bunu farklı bir bakış açısıyla tarif edeyim. Yukarıda gösterdiğim örneğe göre bir hikaye oluşturalım;

Program, Makine ve IMachine hikayemizin aktörleridir. Program çalıştırmak istiyor ama bu yeteneği yok ve Makine nasıl çalışacağını biliyor. Machine ve IMachine en iyi dostlardır ancak Program Machine ile konuşma konusunda değildir. Böylece Program ve IMachine bir anlaşma yapar ve IMachine'ın Programa Makine'ye nasıl bakacağını söyler (bir reflektör gibi).

Program IMachine yardımıyla nasıl çalıştırılacağını öğrenir.

Arayüz iletişim ve gevşek bağlantılı projeler geliştirir.

PS: Özel olarak somut sınıf yöntemim var. Buradaki amacım, somut sınıf özelliklerine ve yöntemlerine erişmeyi engelleyerek gevşek bir şekilde birleşmek ve sadece arayüzler aracılığıyla onlara ulaşmanın yolunu bırakmaktır. (Bu yüzden arayüzlerin yöntemlerini açıkça tanımladım).


1

Biliyorum çok geç kaldım .. (neredeyse dokuz yıl), ama birisi küçük bir açıklama istiyorsa, bunun için gidebilirsiniz:

Basit bir deyişle, bir Nesnenin ne yapabileceğini veya bir nesneye hangi işlevi uygulayacağımızı bildiğinizde Arabirimi kullanırsınız. Örnek Ekleme, Güncelleme ve Silme.

interface ICRUD{
      void InsertData(); // will insert data
      void UpdateData(); // will update data
      void DeleteData(); // will delete data
}

Önemli Not: Arayüzler HER ZAMAN herkese açıktır.

Bu yardımcı olur umarım.

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.