"Arayüzlere programlayın, uygulamalara değil" ne anlama geliyor?


Yanıtlar:


148

Arayüzler sadece sözleşmeler veya imzalardır ve uygulamalar hakkında hiçbir şey bilmiyorlar.

Arayüze karşı kodlama, müşteri kodu her zaman bir fabrika tarafından sağlanan bir Arayüz nesnesini tutar. Fabrika tarafından döndürülen herhangi bir örnek, herhangi bir fabrika aday sınıfının gerçekleştirmiş olması gereken Arayüz türünde olacaktır. Bu şekilde, istemci programı uygulama konusunda endişelenmez ve arayüz imzası tüm işlemlerin neler yapılabileceğini belirler. Bu, bir programın çalışma zamanında davranışını değiştirmek için kullanılabilir. Ayrıca, bakım açısından çok daha iyi programlar yazmanıza yardımcı olur.

İşte size temel bir örnek.

public enum Language
{
    English, German, Spanish
}

public class SpeakerFactory
{
    public static ISpeaker CreateSpeaker(Language language)
    {
        switch (language)
        {
            case Language.English:
                return new EnglishSpeaker();
            case Language.German:
                return new GermanSpeaker();
            case Language.Spanish:
                return new SpanishSpeaker();
            default:
                throw new ApplicationException("No speaker can speak such language");
        }
    }
}

[STAThread]
static void Main()
{
    //This is your client code.
    ISpeaker speaker = SpeakerFactory.CreateSpeaker(Language.English);
    speaker.Speak();
    Console.ReadLine();
}

public interface ISpeaker
{
    void Speak();
}

public class EnglishSpeaker : ISpeaker
{
    public EnglishSpeaker() { }

    #region ISpeaker Members

    public void Speak()
    {
        Console.WriteLine("I speak English.");
    }

    #endregion
}

public class GermanSpeaker : ISpeaker
{
    public GermanSpeaker() { }

    #region ISpeaker Members

    public void Speak()
    {
        Console.WriteLine("I speak German.");
    }

    #endregion
}

public class SpanishSpeaker : ISpeaker
{
    public SpanishSpeaker() { }

    #region ISpeaker Members

    public void Speak()
    {
        Console.WriteLine("I speak Spanish.");
    }

    #endregion
}

alternatif metin

Bu sadece temel bir örnektir ve ilkenin gerçek açıklaması bu cevabın kapsamı dışındadır.

DÜZENLE

Yukarıdaki örneği güncelledim ve soyut bir Speakertemel sınıf ekledim . Bu güncellemede, tüm Konuşmacılara "SayHello" ya bir özellik ekledim. Tüm konuşmacılar "Merhaba Dünya" konuşur. Bu, benzer işleve sahip ortak bir özelliktir. Sınıf şemaya bakın ve bunu bulacaksınız Speakersoyut sınıf uygulamak ISpeakerarayüzü ve işaretleri Speak()her Konuşmacı uygulanması uygulamaktan sorumlu olduğunu hangi vasıta soyut olarak Speak()bu değişir çünkü yöntemi Speakeriçin Speaker. Ancak tüm konuşmacılar oybirliğiyle "Merhaba" diyor. Yani soyut Speaker sınıfında "Merhaba Dünya" yazan bir yöntem tanımlarız ve her Speakeruygulama SayHello()yöntemi türetecektir .

SpanishSpeakerMerhaba diyemeyeceğiniz bir durumu düşünün , bu durumda SayHello()İspanyolca Konuşmacı yöntemini geçersiz kılabilir ve uygun istisna oluşturabilirsiniz.

Lütfen, Interface ISpeaker'da herhangi bir değişiklik yapmadığımızı unutmayın. Ve müşteri kodu ve SpeakerFactory de değişmeden kalır. Ve Arayüze Programlama ile elde ettiğimiz şey budur .

Ve bu davranışı, basit bir temel sınıf Hoparlör ve Her uygulamada bazı küçük değişiklikler ekleyerek ve böylece orijinal programı değiştirmeden bırakarak elde edebilirdik. Bu, herhangi bir uygulamanın istenen bir özelliğidir ve uygulamanızı kolayca bakım yapılabilir hale getirir.

public enum Language
{
    English, German, Spanish
}

public class SpeakerFactory
{
    public static ISpeaker CreateSpeaker(Language language)
    {
        switch (language)
        {
            case Language.English:
                return new EnglishSpeaker();
            case Language.German:
                return new GermanSpeaker();
            case Language.Spanish:
                return new SpanishSpeaker();
            default:
                throw new ApplicationException("No speaker can speak such language");
        }
    }
}

class Program
{
    [STAThread]
    static void Main()
    {
        //This is your client code.
        ISpeaker speaker = SpeakerFactory.CreateSpeaker(Language.English);
        speaker.Speak();
        Console.ReadLine();
    }
}

public interface ISpeaker
{
    void Speak();
}

public abstract class Speaker : ISpeaker
{

    #region ISpeaker Members

    public abstract void Speak();

    public virtual void SayHello()
    {
        Console.WriteLine("Hello world.");
    }

    #endregion
}

public class EnglishSpeaker : Speaker
{
    public EnglishSpeaker() { }

    #region ISpeaker Members

    public override void Speak()
    {
        this.SayHello();
        Console.WriteLine("I speak English.");
    }

    #endregion
}

public class GermanSpeaker : Speaker
{
    public GermanSpeaker() { }

    #region ISpeaker Members

    public override void Speak()
    {
        Console.WriteLine("I speak German.");
        this.SayHello();
    }

    #endregion
}

public class SpanishSpeaker : Speaker
{
    public SpanishSpeaker() { }

    #region ISpeaker Members

    public override void Speak()
    {
        Console.WriteLine("I speak Spanish.");
    }

    public override void SayHello()
    {
        throw new ApplicationException("I cannot say Hello World.");
    }

    #endregion
}

alternatif metin


19
Arayüze programlama sadece referans değişkeninin türü ile ilgili değildir . Ayrıca, uygulamanızla ilgili herhangi bir örtük varsayım kullanmadığınız anlamına gelir. Örneğin List, tür olarak a kullanırsanız , yine de rastgele erişimin art arda arayarak hızlı olduğunu varsayıyor olabilirsiniz get(i).
Joachim Sauer

16
Fabrikalar, arabirimlere programlama yapmak için ortogonaldir, ancak bence bu açıklama, bunun bir parçasıymış gibi görünmesini sağlıyor.
T.

@Toon: sana katılıyorum. Arayüze programlama için çok temel ve basit bir örnek sağlamak istedim. Birkaç kuş ve hayvan sınıfında IFlyable arayüzü uygulayarak soru soran kişinin kafasını karıştırmak istemedim.
bu. __curious_geek

@bu. Bunun yerine soyut bir sınıf veya bir cephe kalıbı kullanırsam, yine de "bir arayüze program" olarak adlandırılacak mı? yoksa açık bir şekilde bir arabirim kullanmalı ve bunu bir sınıfta uygulamalı mıyım?
never_had_a_name

1
Görüntüleri oluşturmak için hangi uml aracını kullandınız?
Adam Arold

29

Arayüzü, bir nesne ile müşterileri arasındaki bir sözleşme olarak düşünün. Yani arayüz, bir nesnenin yapabileceği şeyleri ve bu şeylere erişmek için imzaları belirtir.

Uygulamalar gerçek davranışlardır. Örneğin sort () yönteminiz olduğunu varsayalım. QuickSort veya MergeSort uygulayabilirsiniz. Arayüz değişmediği sürece bu, sort çağıran istemci kodu için önemli olmamalıdır.

Java API ve .NET Framework gibi kitaplıklar, arabirimleri yoğun bir şekilde kullanır çünkü milyonlarca programcı sağlanan nesneleri kullanır. Bu kütüphanelerin yaratıcıları, kütüphaneyi kullanan tüm programcıları etkileyeceği için bu kütüphanelerdeki sınıfların arayüzünü değiştirmemelerine çok dikkat etmelidir. Öte yandan uygulamayı istedikleri kadar değiştirebilirler.

Bir programcı olarak, uygulamaya karşı kod yazarsanız, kod değişir değişmez çalışmayı durdurur. Arayüzün faydalarını şu şekilde düşünün:

  1. bilmeniz gerekmeyen şeyleri gizleyerek nesnenin kullanımını kolaylaştırır.
  2. nesnenin nasıl davranacağına dair sözleşmeyi sağlar, böylece buna güvenebilirsiniz

Bu, yapmak için ne yapacağınızla sözleşme yaptığınızın farkında olmanız gerektiği anlamına gelir: verilen örnekte, yalnızca bir tür için sözleşme yapıyorsunuz, mutlaka istikrarlı bir tür için değil.
penguat

Kütüphane dokümantasyonunun uygulamadan bahsetmemesine benzer şekilde, bunlar sadece dahil edilen sınıf arayüzlerinin açıklamalarıdır.
Joe Iddon

17

Bu, kodunuzu doğrudan uygulama yerine bir soyutlama (soyut sınıf veya arayüz) kullanması için yazmaya çalışmanız gerektiği anlamına gelir.

Normalde uygulama, kurucu veya bir yöntem çağrısı aracılığıyla kodunuza enjekte edilir. Dolayısıyla, kodunuz arayüzü veya soyut sınıfı bilir ve bu sözleşmede tanımlanan her şeyi çağırabilir. Gerçek bir nesne (arayüz / soyut sınıfın uygulanması) kullanıldığında, çağrılar nesne üzerinde çalışır.

Bu, ilkelerin Liskov Substitution PrincipleL'si olan (LSP) ' nin bir alt kümesidir SOLID.

.NET'teki bir örnek IList, Listveya yerine kodlama Dictionaryolabilir, böylece kodunuzda birbirinin yerine uygulayan herhangi bir sınıfı kullanabilirsiniz IList:

// myList can be _any_ object that implements IList
public int GetListCount(IList myList)
{
    // Do anything that IList supports
    return myList.Count();
}

Temel Sınıf Kitaplığından (BCL) bir başka örnek, ProviderBasesoyut sınıftır - bu, bir miktar altyapı sağlar ve daha da önemlisi, ona karşı kod yazarsanız tüm sağlayıcı uygulamalarının birbirinin yerine kullanılabileceği anlamına gelir.


ancak bir müşteri bir arayüzle nasıl etkileşime girebilir ve boş yöntemlerini kullanabilir?
never_had_a_name

1
İstemci arayüzle etkileşime girmez, ancak arayüz aracılığıyla :) Nesneler, yöntemler (mesajlar) aracılığıyla diğer nesnelerle etkileşime girer ve bir arayüz bir tür dildir - belirli bir nesnenin (kişinin) İngilizce (IList) uyguladığını (konuştuğunu) bildiğinizde ), o nesne hakkında daha fazla bilgi sahibi olmaya ihtiyaç duymadan kullanabilirsiniz (o da bir İtalyan), çünkü bu bağlamda gerekli değildir (yardım istemek istiyorsanız, onun İtalyanca da konuştuğunu bilmenize gerek yoktur. İngilizce anlıyorsanız).
Gabriel Ščerbák

BTW. IMHO Liskov ikame ilkesi, kalıtımın anlamıyla ilgilidir ve kalıtım içermeyen dillerde de bulunabilen arayüzlerle hiçbir ilgisi yoktur (Go to Google).
Gabriel Ščerbák

5

Yanmalı Araba döneminde bir Araba Sınıfı yazacak olsaydınız, bu Sınıfın bir parçası olarak oilChange () 'i uygulama şansınız çok yüksektir. Ancak, elektrikli otomobiller piyasaya sürüldüğünde, bu arabalara yağ değişimi yapılmadığı ve uygulama olmadığı için başınız belaya girecektir.

Sorunun çözümü, Car sınıfında bir performMain Maintenance () Arayüzüne sahip olmak ve ayrıntıları uygun uygulama içinde gizlemektir. Her Araba türü performMaincare () için kendi uygulamasını sağlayacaktır. Bir Araba sahibi olarak uğraşmanız gereken tek şey performMaincare () ve bir DEĞİŞİKLİK olduğunda adapte olma konusunda endişelenmeyin.

class MaintenanceSpecialist {
    public:
        virtual int performMaintenance() = 0;
};

class CombustionEnginedMaintenance : public MaintenanceSpecialist {
    int performMaintenance() { 
        printf("combustionEnginedMaintenance: We specialize in maintenance of Combustion engines \n");
        return 0;
    }
};

class ElectricMaintenance : public MaintenanceSpecialist {
    int performMaintenance() {
        printf("electricMaintenance: We specialize in maintenance of Electric Cars \n");
        return 0;
    }
};

class Car {
    public:
        MaintenanceSpecialist *mSpecialist;
        virtual int maintenance() {
            printf("Just wash the car \n");
            return 0;
        };
};

class GasolineCar : public Car {
    public: 
        GasolineCar() {
        mSpecialist = new CombustionEnginedMaintenance();
        }
        int maintenance() {
        mSpecialist->performMaintenance();
        return 0;
        }
};

class ElectricCar : public Car {
    public: 
        ElectricCar() {
             mSpecialist = new ElectricMaintenance();
        }

        int maintenance(){
            mSpecialist->performMaintenance();
            return 0;
        }
};

int _tmain(int argc, _TCHAR* argv[]) {

    Car *myCar; 

    myCar = new GasolineCar();
    myCar->maintenance(); /* I dont know what is involved in maintenance. But, I do know the maintenance has to be performed */


    myCar = new ElectricCar(); 
    myCar->maintenance(); 

    return 0;
}

Ek açıklama: Birden fazla araca sahip olan bir araba sahibisiniz. Dış kaynak olarak kullanmak istediğiniz hizmeti siz belirlersiniz. Bizim durumumuzda, tüm arabaların bakım işlerini dış kaynak olarak kullanmak istiyoruz.

  1. Tüm arabalarınız ve servis sağlayıcılarınız için geçerli olan sözleşmeyi (Arayüz) belirlersiniz.
  2. Hizmet sağlayıcılar, hizmeti sağlamak için bir mekanizma ortaya çıkarırlar.
  3. Araç türünü servis sağlayıcıyla ilişkilendirme konusunda endişelenmek istemezsiniz. Siz sadece bakımı ne zaman planlayacağınızı ve ne zaman çalıştıracağınızı belirtirsiniz. Uygun servis şirketi devreye girmeli ve bakım işini yapmalıdır.

    Alternatif yaklaşım.

  4. Tüm arabalarınız için faydalı olan işi (yeni bir arayüz Arayüzü olabilir) tanımlarsınız.
  5. Sen hizmet sunmak için bir mekanizma ile çıkıyor. Temelde uygulamayı sağlayacaksınız.
  6. İşi çağırır ve kendiniz yaparsınız. Burada uygun bakım işini yapacaksınız.

    2. yaklaşımın dezavantajı nedir? Bakımı yapmanın en iyi yolunu bulma konusunda uzman olmayabilirsiniz. Senin işin arabayı sürmek ve tadını çıkarmak. Onu sürdürme işinde olmamak.

    İlk yaklaşımın dezavantajı nedir? Bir şirket bulmanın ek yükü vardır. Araba kiralama şirketi değilseniz, bu çabaya değmeyebilir.


4

Bu ifade, eşleştirme ile ilgilidir. Nesne yönelimli programlamayı kullanmanın olası bir nedeni yeniden kullanımdır. Örneğin, algoritmanızı iki ortak çalışma nesnesi A ve B arasında bölebilirsiniz. Bu, daha sonra iki nesneden birini veya diğerini yeniden kullanabilecek başka bir algoritmanın oluşturulması için yararlı olabilir. Ancak, bu nesneler iletişim kurduğunda (mesaj gönder - yöntemleri çağır), birbirleri arasında bağımlılıklar oluştururlar. Ancak birini diğeri olmadan kullanmak istiyorsanız, B'yi değiştirirsek, başka bir C nesnesinin A nesnesi için ne yapması gerektiğini belirlemeniz gerekir. Bu açıklamalara arayüzler denir. Bu, A nesnesinin arayüze bağlı olarak farklı nesnelerle değişmeden iletişim kurmasını sağlar. Bahsettiğiniz ifade, bir algoritmanın (veya daha genel olarak bir programı) bir kısmını yeniden kullanmayı planlıyorsanız, arayüzler oluşturmanız ve bunlara güvenmeniz gerektiğini söylüyor.


2

Diğerlerinin de söylediği gibi, bu, çağıran kodunuzun, işi yapacak gerçek uygulama sınıfı DEĞİL, yalnızca soyut bir ebeveyn hakkında bilgi sahibi olması gerektiği anlamına gelir.

Bunu anlamaya yardımcı olan şey, NEDEN her zaman bir arayüze programlamanız gerektiğidir. Pek çok neden var, ancak açıklaması en kolay iki neden

1) Test etme.

Diyelim ki tüm veritabanı kodum tek bir sınıfta olsun. Programım somut sınıfı biliyorsa, kodumu yalnızca o sınıfa karşı gerçekten çalıştırarak test edebilirim. "Konuşur" demek için -> kullanıyorum.

WorkerClass -> DALClass Ancak, karışıma bir arayüz ekleyelim.

WorkerClass -> IDAL -> DALClass.

DALClass IDAL arabirimini uygular ve işçi sınıfı YALNIZCA bunun üzerinden çağırır.

Şimdi, kod için testler yazmak istersek, bunun yerine sadece bir veritabanı gibi davranan basit bir sınıf yapabiliriz.

WorkerClass -> IDAL -> IFakeDAL.

2) Yeniden Kullanım

Yukarıdaki örnekten sonra, diyelim ki SQL Server'dan (somut DALClass'ın kullandığı) MonogoDB'ye geçmek istiyoruz. Bu büyük bir iş gerektirir, ancak bir arayüze programlamış olsaydık DEĞİL. Bu durumda sadece yeni DB sınıfını yazarız ve değiştiririz (fabrika aracılığıyla)

WorkerClass -> IDAL -> DALClass

için

WorkerClass -> IDAL -> MongoDBClass


1

arayüzler yetenekleri tanımlar. zorunlu kod yazarken, belirli türler veya sınıflar yerine, kullandığınız yeteneklerden bahsedin.

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.