it-swarm.dev

Soyut bir sınıf yerine bir arayüz ne zaman kullanılır?

Bu genel bir OOP sorusu olabilir. Bir arayüz ile soyut bir sınıf arasında kullanımlarına göre genel bir karşılaştırma yapmak istedim. 

Ne zaman bir arayüz kullanmak ister ve ne zaman bir soyut sınıf kullanmak ister?

374
Chirantan

Bununla ilgili bir makale yazdım:

Soyut sınıflar ve arayüzler

Özetleme:

Soyut sınıflardan bahsederken, bir nesne türünün özelliklerini tanımlarız; bir nesnenin ne olduğunu belirtir.

Bir arayüz hakkında konuştuğumuzda ve sağlamaya söz verdiğimiz yetenekleri tanımladığımızda, nesnenin neler yapabileceği hakkında bir kontrat oluşturmaktan bahsediyoruz nesnenin ne yapabileceği hakkında.

405
Jorge Córdoba

Soyut bir sınıf paylaşılan duruma veya işlevselliğe sahip olabilir. Bir arayüz sadece devlet veya işlevsellik sağlama sözüdir. İyi bir soyut sınıf, yeniden yazılması gereken kod miktarını azaltacaktır çünkü işlevselliği veya durumu paylaşılabilir. Arayüzde paylaşılacak tanımlanmış bilgi yok

394
Alex

Şahsen, neredeyse hiçbir zaman soyut dersler yazma ihtiyacım yok.

Çoğu zaman soyut sınıfların kullanıldığını (yanlış) görüyorum, çünkü soyut sınıfın yazarı "Şablon yöntemi" desenini kullanıyor.

"Şablon yöntemi" ile ilgili sorun, hemen hemen her zaman bir şekilde yeniden giriş yapan olmasıdır - "türetilmiş" sınıf, uyguladığı temel sınıfın yalnızca "soyut" yöntemini değil, aynı zamanda temel sınıfın genel yöntemlerini de bilir. , çoğu zaman onları aramanıza gerek kalmaz.

(Aşırı basitleştirilmiş) örnek:

abstract class QuickSorter
{
    public void Sort(object[] items)
    {
        // implementation code that somewhere along the way calls:
        bool less = compare(x,y);
        // ... more implementation code
    }
    abstract bool compare(object lhs, object rhs);
}

İşte burada, bu sınıfın yazarı genel bir algoritma yazdı ve insanların kendi "kancalarını" sağlayarak "uzmanlaştırarak" kullanmalarını istiyor - bu durumda, "karşılaştır" yöntemi.

Yani amaçlanan kullanım şunun gibi bir şeydir:

class NameSorter : QuickSorter
{
    public bool compare(object lhs, object rhs)
    {
        // etc.
    }
}

Bununla ilgili sorun, iki kavramı haksız yere birleştirmiş olmanızdır:

  1. İki maddeyi karşılaştırmanın bir yolu (ilk önce hangi maddenin gitmesi gerektiği)
  2. Öğeleri sıralama yöntemi (örneğin quicksort vs birleştirme sıralaması vs.)

Yukarıdaki kodda, teorik olarak, "kıyaslama" yönteminin yazarı yeniden entrantly "yeniden sınıflandırma" yöntemine geri dönebilir ... pratikte bunu yapmak istemeyecekleri veya ihtiyaç duymasalar bile.

Bu gereksiz eşleşme için ödediğiniz fiyat, üst sınıfı değiştirmenin zor olması ve çoğu OO dilde çalışma zamanında değiştirmek imkansız.

Alternatif yöntem bunun yerine "Strateji" tasarım modelini kullanmaktır:

interface IComparator
{
    bool compare(object lhs, object rhs);
}

class QuickSorter
{
    private readonly IComparator comparator;
    public QuickSorter(IComparator comparator)
    {
        this.comparator = comparator;
    }

    public void Sort(object[] items)
    {
        // usual code but call comparator.Compare();
    }
}

class NameComparator : IComparator
{
    bool compare(object lhs, object rhs)
    {
        // same code as before;
    }
}

Şimdi dikkat edin: Elimizdeki tek şey arayüzler ve bu arayüzlerin somut uygulamaları. Uygulamada, üst düzeyde OO tasarım yapmak için başka hiçbir şeye ihtiyacınız yok.

Bir "QuickSort" sınıfı ve "NameComparator" kullanarak "ad sıralama" uyguladığımız gerçeğini "gizlemek" için, yine de bir fabrika yöntemini yazabiliriz:

ISorter CreateNameSorter()
{
    return new QuickSorter(new NameComparator());
}

Herhangi bir soyut bir sınıfa sahipseniz, bunu yapabilirsiniz ... üs ve türetilmiş sınıf arasında doğal bir yeniden girilen ilişki olsa bile, genellikle onları açık hale getirmek için para ödersiniz.

Son bir düşünce: Yukarıda yaptığımız tek şey, bir "QuickSort" işlevini ve "NameComparison" işlevini kullanarak "bir" NameSorting "işlevini" oluşturmak "... işlevsel bir programlama dilinde, bu programlama stili daha da doğal hale geliyor, az kod ile.

75

Tamam, sadece kendime "sıkılmış" olmakla - işte meslekten olmayanlar açısından (yanılıyorsam beni düzeltmekte özgürsün) - bu konunun oooooold olduğunu biliyorum, ama bir gün başka biri bununla karşılaşabilir ...

Soyut sınıflar, bir plan oluşturmanıza olanak tanır ve ayrıca TÜM soylarının sahip olmasını istediğiniz özellikleri ve yöntemleri İNCELEME yapmanıza izin verir.

Öte yandan bir arabirim yalnızca belirli bir adı taşıyan özelliklerin ve/veya yöntemlerin onu uygulayan tüm sınıflarda var olmasını istediğinizi beyan etmenize izin verir - ancak onu nasıl uygulamanız gerektiğini belirtmez. Ayrıca, bir sınıf MANY arayüzlerini uygulayabilir, ancak yalnızca ONE Abstract sınıfını genişletebilir. Arayüz daha yüksek seviyeli bir mimari araçtır (tasarım desenlerini kavramaya başlarsanız daha açık hale gelir) - bir Özet her iki kampta da bir ayağa sahiptir ve kirli çalışmaların bir kısmını da gerçekleştirebilir.

Neden birini diğerinin üzerinde kullanıyorsun? İlki, torunları daha somut tanımına izin verir - ikincisi daha büyük polimorfizm . Bu son nokta, A.P .I (nterface) 'nin ihtiyaçlarını karşılamak için çeşitli kombinasyonlarda/şekillerde bu bilgiyi kullanan son kullanıcı/kodlayıcı için önemlidir.

Bunun benim için "ampul" an olduğunu düşünüyorum - bir projeye uygulama ekleyen zincirde daha sonra gelen herhangi bir kodlayıcıdan daha az yazarın bakış açısıyla olan arayüzleri düşünün ya da Bir API'yi genişletme .

40
sunwukung

Benim iki Sentim:

Bir arayüz temelde, herhangi bir uygulayıcı sınıfın uyması gereken (arayüz üyelerini uygulamak) bir sözleşmeyi tanımlar. Herhangi bir kod içermiyor.

Öte yandan, soyut bir sınıf kod içerebilir ve kalıtımsal bir sınıfın uygulaması gereken soyut olarak işaretlenmiş bazı yöntemler olabilir.

Soyut sınıfları kullandığım nadir durumlar, miras veren sınıfın bazı uzmanlık sınıflarının devraldığı soyut bir temel sınıf, yani devralma konusunda ilginç olmayabilir.

Örnek (çok ilkel bir!): CalculatePayment(), CalculateRewardPoints() gibi soyut yöntemleri ve GetName(), SavePaymentDetails() gibi bazı soyut olmayan yöntemleri olan Müşteri adlı bir temel sınıfı düşünün. 

RegularCustomer ve GoldCustomer gibi özel sınıflar Customer base sınıfından miras alır ve kendi CalculatePayment() ve CalculateRewardPoints() yöntem mantığını uygular, ancak GetName() ve SavePaymentDetails() yöntemlerini yeniden kullanın.

Eski bir sürümü kullanan alt sınıfları etkilemeden, soyut bir sınıfa (soyut olmayan yöntemler) daha fazla işlevsellik ekleyebilirsiniz. Bir arabirime yöntem eklemek, şimdi yeni eklenen arabirim üyelerini uygulamak için gerek duyduğu için onu uygulayan tüm sınıfları etkileyecektir.

Tüm soyut üyelere sahip soyut bir sınıf, bir arayüze benzer olacaktır.

35
PreethaA

Ne zaman ne yapılacağı çok basit bir şeydir, zihninizde açık bir kavram varsa. 

Soyut sınıflar Türetilebilirken Arayüzler Uygulanabilir. İkisi arasında bir fark var. Bir Soyut sınıfı türettiğinizde, türetilmiş sınıfla temel sınıf arasındaki ilişki '' bir 'ilişkidir. örneğin, bir Köpek bir Hayvandır, Bir Koyun bir Hayvandır, yani bir Türetilmiş sınıfın baz sınıftan bazı özellikleri miras aldığı anlamına gelir.

Arabirimlerin uygulanması için, ilişki "olabilir". örneğin, bir Köpek casus bir köpek olabilir. Bir köpek bir sirk köpeği olabilir. Bir köpek yarış köpeği olabilir. Bu, bir şey elde etmek için belirli yöntemleri uyguladığınız anlamına gelir. 

Umarım netimdir.

29
Aamir

Java’ya OOP dili olarak bakıyorsanız,

"arayüz yöntem uygulaması sağlamaz" artık Java 8 ile birlikte geçerli değil. Şimdi Java, varsayılan yöntemler için arayüzde uygulama sağlar.

Basit anlamda kullanmak istiyorum

interface: İlişkili olmayan birden çok nesne tarafından bir sözleşme uygulamak için. "HAS A" yeteneği sağlar. 

abstract class: Birden fazla ilgili nesne arasında aynı veya farklı davranışı uygulamak için. "IS A" ilişkisini kurar.

Oracle website , interface ve abstract sınıfı arasında önemli farklılıklar sağlar.

Soyut sınıfları kullanmayı düşünün eğer:

  1. Kodu, yakından ilgili birkaç sınıf arasında paylaşmak istiyorsunuz.
  2. Soyut sınıfınızı genişleten sınıfların birçok ortak yöntem veya alana sahip olmasını ya da kamu dışındaki (korumalı ve özel) erişim değiştiricileri gerektirmesini beklersiniz.
  3. Statik olmayan veya son olmayan alanları bildirmek istersiniz. 

Arabirimleri kullanmayı düşünün eğer:

  1. İlişkisiz sınıfların arayüzünüzü uygulayacağını beklersiniz. Örneğin, ilişkisiz birçok nesne Serializable arayüzünü uygulayabilir.
  2. Belirli bir veri türünün davranışını belirtmek istiyorsunuz, ancak davranışını kimin uyguladığı ile ilgilenmiyorsunuz.
  3. Birden fazla kalıtım türünden yararlanmak istiyorsunuz.

Örnek:

Soyut sınıf (IS A ilişkisi)

Okuyucu soyut bir sınıftır.

BufferedReader bir Reader

FileReader bir Reader

FileReader ve BufferedReader ortak amaçlar için kullanılır: Verileri okumak ve Reader sınıfı ile ilişkilidir.

Arabirim (HAS A yeteneği)

Seri hale getirilebilir , bir arabirimdir. 

Uygulamanızda Serializable arabirimini uygulayan iki sınıfınız olduğunu varsayalım

Employee implements Serializable

Game implements Serializable

Burada, Serializable ve Employee arasındaki Game arayüzü ile farklı amaçlara yönelik bir ilişki kuramazsınız. Her ikisi de devleti seri hale getirme yeteneğine sahip ve karşılaştırma orada bitiyor.

Şu yayınlara bir göz atın:

Arayüz ve Soyut sınıf arasındaki farkı nasıl açıklamalıyım?

27
Ravindra babu

Soyut bir sınıfın ne zaman ve ne zaman arayüz kullanılacağına dair bir makale yazdım. Aralarında "bir IS-A ... ve bir CAN-DO ..." dışında bir çok fark var. Bana göre, bunlar cevaplandırılmış cevaplar. Bunlardan birini ne zaman kullanacağımın birkaç nedeninden bahsediyorum. Umarım yardımcı olur.

http://codeofdoom.com/wordpress/2009/02/12/learn-this-when-to-use-an-abstract-class-and-an-interface/

10
marcel

1. İlişkili olmayan sınıflara ortak işlevsellik sağlayan bir şey oluşturuyorsanız, bir arabirim kullanın.

2.Hiyerarşiyle yakından ilişkili nesneler için bir şeyler oluşturuyorsanız, soyut bir sınıf kullanın.

10
kuttychutty

Bence onu yazmanın en özlü yolu şudur:

Paylaşılan özellikler => soyut sınıf.
Paylaşılan işlevsellik => arayüz.

Ve daha az kısaca söylemek gerekirse ...

Soyut Sınıf Örneği:  

public abstract class BaseAnimal
{
    public int NumberOfLegs { get; set; }

    protected BaseAnimal(int numberOfLegs)
    {
        NumberOfLegs = numberOfLegs;
    }
}

public class Dog : BaseAnimal
{
    public Dog() : base(4) { }
}

public class Human : BaseAnimal 
{
    public Human() : base(2) { }
}

Hayvanlar ortak bir özelliğe sahip olduklarından - bu durumda bacak sayısı - bu ortak özelliği içeren soyut bir sınıf yapmak mantıklıdır. Bu aynı zamanda bu özellik üzerinde çalışan ortak kodlar yazmamıza izin veriyor. Örneğin:

public static int CountAllLegs(List<BaseAnimal> animals)
{
    int legCount = 0;
    foreach (BaseAnimal animal in animals)
    {
        legCount += animal.NumberOfLegs;
    }
    return legCount;
}

Arayüz Örneği:

public interface IMakeSound
{
    void MakeSound();
}

public class Car : IMakeSound
{
    public void MakeSound() => Console.WriteLine("Vroom!");
}

public class Vuvuzela : IMakeSound
{
    public void MakeSound() => Console.WriteLine("VZZZZZZZZZZZZZ!");        
}

Burada Vuvuzelas ve Arabaların tamamen farklı şeyler olduğuna dikkat edin, ancak paylaşılan işlevselliğe sahip olduklarını: bir ses çıkarmak. Böylece, bir arayüz burada mantıklı. Dahası, programcıların ortak bir arabirim altında sesleri birleştiren şeyleri gruplamalarına izin verecektir - bu durumda IMakeSound. Bu tasarım ile aşağıdaki kodu yazabilirsiniz:

List<IMakeSound> soundMakers = new List<ImakeSound>();
soundMakers.Add(new Car());
soundMakers.Add(new Vuvuzela());
soundMakers.Add(new Car());
soundMakers.Add(new Vuvuzela());
soundMakers.Add(new Vuvuzela());

foreach (IMakeSound soundMaker in soundMakers)
{
    soundMaker.MakeSound();
}

Bunun neyin çıktığını söyleyebilir misiniz?

Son olarak, ikisini birleştirebilirsiniz.

Kombine Örnek:

public interface IMakeSound
{
    void MakeSound();
}

public abstract class BaseAnimal : IMakeSound
{
    public int NumberOfLegs { get; set; }

    protected BaseAnimal(int numberOfLegs)
    {
        NumberOfLegs = numberOfLegs;
    }

    public abstract void MakeSound();
}

public class Cat : BaseAnimal
{
    public Cat() : base(4) { }

    public override void MakeSound() => Console.WriteLine("Meow!");
}

public class Human : BaseAnimal 
{
    public Human() : base(2) { }

    public override void MakeSound() => Console.WriteLine("Hello, world!");
}

Burada tüm BaseAnimal'ların ses çıkarmasını istiyoruz, ancak henüz uygulanmasını bilmiyoruz. Böyle bir durumda, arayüz uygulamasını soyutlayabilir ve uygulamayı alt sınıflarına devredebiliriz.

Son bir nokta olarak, soyut sınıf örneğinde farklı nesnelerin paylaşılan özellikleri üzerinde nasıl çalıştığımızı ve arayüz örneğinde farklı nesnelerin paylaşılan işlevlerini nasıl kullanabileceğimizi hatırlıyor musunuz? Bu son örnekte ikisini de yapabilirdik. 

4
Domn Werner

Durumunuzda bu ifadelerden herhangi biri geçerliyse abstract sınıflarını kullanın.

  1. Kodu, yakından ilgili birkaç sınıf arasında paylaşmak istiyorsunuz.
  2. Soyut sınıfınızı genişleten sınıfların birçok ortak yöntem veya alana sahip olmasını ya da kamu dışındaki (korumalı ve özel) erişim değiştiricileri gerektirdiğini düşünüyorsunuz.
  3. Statik olmayan veya son olmayan alanları bildirmek istersiniz. Bu, ait oldukları nesnenin durumuna erişebilecek ve bunları değiştirebilecek yöntemleri tanımlamanızı sağlar.

Bu ifadelerden herhangi biri durumunuz için geçerliyse interface kullanmayı düşünün:

  1. İlişkisiz sınıfların arayüzünüzü uygulayacağını beklersiniz. Örneğin, Kıyaslanabilir ve Klonlanabilir arayüzler, pek çok ilgisiz sınıf tarafından uygulanır.
  2. Belirli bir veri türünün davranışını belirtmek istiyorsunuz, ancak davranışını kimin uyguladığı ile ilgilenmiyorsunuz.
  3. Birden fazla mirastan yararlanmak istiyorsunuz.

Kaynak

Sınıflar yalnızca bir temel sınıftan miras alabilir; bu nedenle, bir sınıf grubuna polimorfizm sağlamak için soyut sınıfları kullanmak istiyorsanız, hepsinin o sınıftan miras alması gerekir. Soyut sınıflar, zaten uygulanmış olan üyeler de sağlayabilir. Bu nedenle, soyut bir sınıfla belirli miktarda özdeş işlevsellik sağlayabilirsiniz, ancak arayüzle olamaz. 

Bileşenleriniz için polimorfizm sağlamak için bir arayüz veya soyut bir sınıf kullanıp kullanmamaya karar vermenize yardımcı olacak bazı öneriler.

  • Bileşeninizin birden fazla versiyonunu oluşturmayı düşünüyorsanız, soyut bir sınıf oluşturun. Soyut sınıflar, bileşenlerinizi sürümlendirmenin basit ve kolay bir yolunu sağlar. Temel sınıfı güncelleyerek, miras alan tüm sınıflar değişiklikle otomatik olarak güncellenir. Öte yandan, arayüzler bu şekilde oluşturulduktan sonra değiştirilemez. Arabirimin yeni bir sürümü gerekliyse, tamamen yeni bir arabirim oluşturmanız gerekir . 
  • Yarattığınız işlevsellik çok çeşitli nesneler arasında yararlı olacaksa, bir arabirim kullanın. Soyut sınıflar öncelikle yakından ilgili nesneler için kullanılmalıdır, oysa ara yüzler ilgisiz sınıflara ortak işlevsellik sağlamak için çok uygundur . 
  • Küçük, özlü işlevsellik parçaları tasarlıyorsanız, arayüzleri kullanın. Büyük fonksiyonel birimler tasarlıyorsanız, soyut bir sınıf kullanın . 
  • Bileşeninizin tüm uygulamaları arasında ortak, uygulanmış işlevsellik sağlamak istiyorsanız, soyut bir sınıf kullanın. Soyut sınıflar, sınıfınızı kısmen uygulamanıza izin verirken, arayüzler hiçbir üyeye uygulama içermez.

: 'Den kopyalandı.
http://msdn.Microsoft.com/en-us/library/scsyfw1d%28v=vs.71%29.aspx

3
Mazhar

Benim için birçok durumda arayüzlerle giderdim. Fakat bazı durumlarda soyut dersleri tercih ederim.

OO sınıfları, genel olarak uygulamaya atıfta bulunur. Bazı uygulama detaylarını ara yüzlerle gittiğim diğer çocuklara zorlamak istediğimde soyut sınıfları kullanıyorum.

Elbette, soyut sınıflar sadece uygulamaya zorlamada değil, aynı zamanda birçok ilgili sınıf arasında belirli bazı ayrıntıları paylaşmada da faydalıdır.

2
La VloZ Merrill

Cevaplar diller arasında değişir. Örneğin, Java'da bir sınıf birden çok arabirim uygulayabilir (mirastan) ancak yalnızca bir soyut sınıftan miras alabilir. Böylece arayüzler size daha fazla esneklik kazandırır. Ancak bu C++ 'da doğru değildir.

2
Nick Fortescue

Bazı temel uygulamalar sağlamak istiyorsanız soyut bir sınıf kullanın.

1
Sebastian

java'da, bir (soyut) sınıftan "işlevselliği" sağlamak için miras alabilir ve işlevselliği "sağlamak" için birçok arabirim uygulayabilirsiniz.

1
Peter Miehle

Arayüzlerin soyut sınıflardan daha iyi ücretlendirdiği ilginç bir konum, bir grup (ilişkili veya ilgisiz) nesneye ekstra işlevler eklemeniz gerektiği zamandır. Onlara temel bir soyut sınıf veremezseniz (örneğin, sealed veya zaten bir ebeveyni var), onlara kukla (boş) bir arabirim verebilir ve ardından bu arabirim için uzatma yöntemleri yazabilirsiniz.

1
Dmitri Nesteruk

Tamamen miras temelinde, açıkça soydan, soyut ilişkiler (yani hayvan-> kedi) tanımladığınız ve/veya sanal veya kamuya açık olmayan mülklerin miras almasını gerektiren, özellikle paylaşılan durumları (veya paylaşılanları) içeren bir Özet kullanırsınız. Hangi Arayüzleri destekleyemez).

Yapabildiğiniz yerde kalıtım yerine kompozisyon bağımlılığını (bağımlılık enjeksiyonu yoluyla) denemeli ve tercih etmelisiniz ve sözleşmelere giren Arayüzlerin birim testini, kaygıların ayrılmasını ve (dile göre değişen) çoklu kalıtımın Özetler'in yapamayacağı şekilde desteklediğini not almalısınız.

1
annakata

Arayüz yerine soyut bir sınıfı ne zaman tercih edersiniz?

  1. Bir program/projenin ömrü boyunca bir temel sınıfı güncellemeyi planlıyorsa, temel sınıfın soyut bir sınıf olmasına izin vermek en iyisidir.
  2. Bir hiyerarşi içinde yakından ilişkili nesneler için bir omurga inşa etmeye çalışıyorsa, soyut bir sınıf kullanmak son derece yararlıdır

Soyut sınıf yerine arayüz ne zaman tercih edilir?

  1. Eğer biri büyük bir hiyerarşik çerçeve türü ile uğraşmazsa, arayüzler mükemmel bir seçim olacaktır.
  2. Birden fazla kalıtım soyut sınıflarla desteklenmediğinden (elmas problemi), arayüzler günü kurtarabilir 
1
Satya

Bu yapmak için çok zor bir çağrı olabilir ...

Bir işaretçi verebilirim: Bir nesne birçok arabirimi uygulayabilir, bir nesne yalnızca bir temel sınıfı miras alabilirken (c # gibi modern bir OO dilinde, C++ 'ın çoklu kalıtımsal olduğunu biliyorum - ama kaşlarını çattıyor mu? )

0
Mesh

Soyut bir sınıfın uygulamaları olabilir.

Bir arayüzün uygulamaları yoktur, sadece bir çeşit sözleşme tanımlar.

Ayrıca dile bağlı bazı farklılıklar da olabilir: örneğin C # çoklu kalıtım seviyesine sahip değildir, ancak bir sınıfa birden fazla arayüz uygulanabilir.

0
Gerrie Schenck