Bu genel bir OOP sorusu olabilir. Kullanımları temelinde bir arayüz ile soyut bir sınıf arasında genel bir karşılaştırma yapmak istedim.
Ne zaman bir arayüz ve ne zaman soyut bir sınıf kullanmak istersiniz ?
Bu genel bir OOP sorusu olabilir. Kullanımları temelinde bir arayüz ile soyut bir sınıf arasında genel bir karşılaştırma yapmak istedim.
Ne zaman bir arayüz ve ne zaman soyut bir sınıf kullanmak istersiniz ?
Yanıtlar:
Bununla ilgili bir makale yazdım:
Özetleme:
Soyut sınıflar hakkında konuştuğumuzda, bir nesne tipinin özelliklerini tanımlarız; bir nesnenin ne olduğunu belirleme .
Bir arayüz hakkında konuştuğumuzda ve sunmayı vaat ettiğimiz yetenekleri tanımladığımızda , nesnenin neler yapabileceği hakkında bir sözleşme oluşturmaktan bahsediyoruz .
Interfaces do not express something like "a Doberman is a type of dog and every dog can walk" but more like "this thing can walk"
. Teşekkür ederim
Use abstract classes and inheritance if you can make the statement “A is a B”. Use interfaces if you can make the statement “A is capable of [doing] as”
Soyut bir sınıfın durumu veya işlevselliği paylaşılan olabilir. Bir arabirim yalnızca durumu veya işlevselliği sağlama vaadidir. İyi bir soyut sınıf, işlevselliği veya durumu paylaşılabildiği için yeniden yazılması gereken kod miktarını azaltacaktır. Arabirimin paylaşılacak tanımlanmış bir bilgisi yok
Şahsen, neredeyse hiç soyut ders yazma ihtiyacım olmadı.
Çoğu zaman soyut sınıfların (mis) kullanıldığını görüyorum, çünkü soyut sınıfın yazarı "Şablon yöntemi" desenini kullanıyor.
"Şablon yöntemi" ile ilgili sorun, neredeyse her zaman biraz yeniden girilmesidir - "türetilmiş" sınıf, temel sınıfının uyguladığı "soyut" yöntemi değil, aynı zamanda temel sınıfın genel yöntemlerini de bilir. , çoğu zaman onları aramak zorunda olmasa da.
(Aşırı basitleştirilmiş) örneği:
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);
}
Yani burada, bu sınıfın yazarı genel bir algoritma yazmıştır ve insanların kendi "kancalarını" sağlayarak "uzmanlaştırarak" - bu durumda bir "karşılaştırma" yöntemi kullanmasını amaçlamaktadır.
Yani amaçlanan kullanım şöyle:
class NameSorter : QuickSorter
{
public bool compare(object lhs, object rhs)
{
// etc.
}
}
Buradaki sorun, iki kavramı gereğince birleştirmiş olmanızdır:
Yukarıdaki kodda, teorik olarak, "karşılaştır" yönteminin yazarı, pratikte bunu asla istemeyecek veya yapmayacak olsa bile , yeniden üst sınıf "Sırala" yöntemine geri çağırabilir.
Bu gereksiz kuplaj için ödediğiniz fiyat, üst sınıfı değiştirmenin zor olması ve çoğu OO dilinde, çalışma zamanında değiştirmek imkansız olmasıdır.
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: Sahip olduğumuz tek şey arayüzler ve bu arayüzlerin somut uygulamaları. Uygulamada, üst düzey bir OO tasarımı yapmak için başka bir şeye ihtiyacınız yoktur.
Bir "QuickSort" sınıfı ve bir "NameComparator" kullanarak "adların sıralanmasını" uyguladığımız gerçeğini "gizlemek" için, yine de bir yere bir fabrika yöntemi yazabiliriz:
ISorter CreateNameSorter()
{
return new QuickSorter(new NameComparator());
}
Herhangi tabanı ve türetilmiş sınıfın arasında doğal yeniden giriş ilişki olduğunu bile bunu yapabilirsiniz soyut sınıf var zaman ..., genellikle onları açık hale getirmek için öder.
Son bir düşünce: Yukarıda yaptığımız tek şey, bir "QuickSort" işlevi ve bir "NameComparison" işlevi kullanarak bir "NameSorting" işlevi "oluşturmak" dır ... işlevsel bir programlama dilinde, bu programlama tarzı daha doğal hale gelir, daha az kodla.
Java'ya OOP dili olarak bakıyorsanız,
" arabirim yöntem uygulaması sağlamaz " artık Java 8 lansmanı ile geçerli değildir. Java, varsayılan yöntemler için arayüzde uygulama sağlar.
Basit bir ifadeyle, kullanmak istiyorum
interface: İlişkisiz birden çok nesne tarafından bir sözleşme uygulamak. " HAS A " yeteneği sağlar.
soyut sınıf: Aynı veya farklı davranışı birden çok ilişkili nesne arasında uygulamak. " IS A " ilişkisinikurar.
Oracle web sitesiinterface
ve abstract
sınıf arasındaki önemli farkları sağlar .
Aşağıdaki durumlarda soyut sınıfları kullanmayı düşünün :
Aşağıdaki durumlarda arayüzleri kullanmayı düşünün :
Serializable
arabirim uygulayabilir .Misal:
Soyut sınıf ( IS A ilişkisi)
Okuyucu soyut bir sınıftır.
BufferedReader birReader
FileReader birReader
FileReader
ve BufferedReader
ortak amaç için kullanılır: Verileri okumak ve bunlar Reader
sınıf yoluyla ilişkilidir .
Arayüz ( HAS A yeteneği)
Serileştirilebilir bir arabirimdir.
Uygulamanızda Serializable
arayüz uygulayan iki sınıfınız olduğunu varsayın
Employee implements Serializable
Game implements Serializable
Burada , farklı amaçlar için olan ve Serializable
arasındaki arayüz aracılığıyla herhangi bir ilişki kuramazsınız. Her ikisi de devleti seri hale getirme yeteneğine sahiptir ve karşılaştırma burada biter.Employee
Game
Şu yayınlara bir göz atın:
Bir Interface ve Abstract sınıfı arasındaki farkı nasıl açıklamalıydım?
Tamam, sadece kendimi "grokked" olması - burada layman açısından (eğer yanılıyorsam beni düzeltmekten çekinmeyin) - Bu konunun oooooold olduğunu biliyorum, ama bir gün başka biri rastlamak olabilir ...
Soyut sınıflar, bir plan oluşturmanıza ve TÜM torunlarının sahip olmasını istediğiniz özellikleri ve yöntemleri ayrıca İNŞAAT (uygulamanızı) sağlar.
Öte yandan bir arabirim, yalnızca belirli bir ada sahip özelliklerin ve / veya yöntemlerin onu uygulayan tüm sınıflarda olmasını istediğinizi bildirmenize izin verir - ancak bunu nasıl uygulamanız gerektiğini belirtmez. Ayrıca, bir sınıf MANY arabirimlerini uygulayabilir, ancak yalnızca ONE Abstract sınıfını genişletebilir. Bir Arabirim daha üst düzey bir mimari araçtır (tasarım desenlerini kavramaya başlarsanız daha net olur) - Bir Özet'in her iki kampta da bir ayağı vardır ve kirli işlerin bir kısmını da yapabilir.
Neden birini diğerinin üzerinde kullanıyorsunuz? Birincisi torunların daha somut bir tanımına izin verir - ikincisi daha fazla polimorfizme izin verir . Bu son nokta, AP I'yi (arayüz) ihtiyaçlarına uygun çeşitli kombinasyonlarda / şekillerde uygulamak için bu bilgiyi kullanabilen son kullanıcı / kodlayıcı için önemlidir .
Bu benim için "ampul" anıydı - yazarın bakış açısından daha az ve daha sonra bir projeye uygulama ekleyen veya bir API genişleten zincirde daha sonra gelen herhangi bir kodlayıcının arayüzlerini düşünün .
Benim görüşüm:
Bir arayüz temel olarak, herhangi bir uygulayıcı sınıfın uyması gereken bir arayüz tanımlar (arayüz üyelerini uygulamak). Herhangi bir kod içermiyor.
Öte yandan, soyut bir sınıf kod içerebilir ve miras olarak atanan bir sınıfın uygulaması gereken soyut olarak işaretlenmiş bazı yöntemler olabilir.
Soyut sınıfları kullandığım nadir durumlar, miras sınıfının, bazı temel sınıfların miras aldığı geçersiz bir temel sınıfın geçersiz kılmasında ilginç olmayabileceği bazı varsayılan işlevlere sahip olduğumdur.
Örnek (çok ilkel bir!): Gibi soyut yöntemleri vardır bir temel sınıf olarak adlandırılan Müşteriyi düşünün CalculatePayment()
, CalculateRewardPoints()
ve benzeri olmayan bazı soyut yöntemler GetName()
, SavePaymentDetails()
.
Gibi sınıflar uzmanlaşmış RegularCustomer
ve GoldCustomer
gelen devralır Customer
temel sınıf ve kendi uygulamak CalculatePayment()
ve CalculateRewardPoints()
yöntem mantığı, ancak yeniden kullanmak GetName()
ve SavePaymentDetails()
yöntemleri.
Daha 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, yeni eklenen arabirim üyelerini uygulamak zorunda oldukları için onu uygulayan tüm sınıfları etkileyecektir.
Tüm soyut üyeleri içeren soyut bir sınıf bir arayüze benzer.
Zihninizde açık bir kavram varsa ne zaman ne yapacağınız çok basit bir şeydir.
Soyut sınıflar Türetilebilirken, Arayüzler Uygulanabilir. İkisi arasında bir fark var. Bir Abstract sınıfı türettiğinizde, türetilmiş sınıf ile temel sınıf arasındaki ilişki 'bir' ilişkidir. örneğin, bir köpek bir hayvandır, bir koyun bir hayvandır, bu türetilmiş bir sınıfın bazı özellikleri temel sınıftan miras aldığı anlamına gelir.
Arayüzlerin 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 bir yarış köpeği olabilir. Bu, bir şey elde etmek için belirli yöntemleri uyguladığınız anlamına gelir.
Umarım netimdir.
İlişkisiz 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.
Ne zaman soyut bir sınıf kullanılacağı ve ne zaman bir arayüz kullanılacağı hakkında bir makale yazdım. Aralarında "bir IS-A ... ve bir CAN-DO ..." dışında çok daha fazla fark var. Bana göre, bunlar hazır cevaplar. İkisinden birini kullanmanın birkaç nedeninden bahsediyorum. Umarım yardımcı olur.
Bunu koymanın en kısa yolu şöyle düşünüyorum:
Paylaşılan özellikler => soyut sınıf.
Paylaşılan işlevsellik => arayüz.
Ve daha az öz vermek 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 paylaşılan özelliği içeren soyut bir sınıf yapmak mantıklıdır. Bu aynı zamanda söz konusu mülk üzerinde çalışan ortak kod yazmamızı sağlar. Ö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 Cars'ın tamamen farklı şeyler olduğunu, ancak paylaştıkları işlevselliğe sahip olduklarını unutmayın: bir ses çıkarmak. Böylece, burada bir arayüz mantıklı. Ayrıca, programcıların ses çıkaran şeyleri ortak bir arayüz altında bir araya getirmelerine izin verecektir - IMakeSound
bu durumda. Bu tasarımla 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 ne elde edeceğini 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, herkesin BaseAnimal
ses çıkarmasını istiyoruz, ancak henüz uygulanmasını bilmiyoruz. Böyle bir durumda arayüz uygulamasını soyutlayabilir ve uygulamasını alt sınıflarına devredebiliriz.
Son bir nokta, soyut sınıf örneğinde farklı nesnelerin paylaşılan özellikleri üzerinde nasıl çalışabildiğimizi ve arayüz örneğinde farklı nesnelerin paylaşılan işlevselliğini nasıl çağırabildiğimizi hatırlıyor musunuz? Bu son örnekte her ikisini de yapabiliriz.
Arayüz üzerinden soyut bir sınıf ne zaman tercih edilmeli?
Soyut sınıfa göre ne zaman bir arayüz tercih edilmeli?
Sınıflar yalnızca bir temel sınıftan miras alabilir, bu nedenle bir grup sınıfa polimorfizm sağlamak için soyut sınıfları kullanmak istiyorsanız, hepsi bu sınıftan miras almalıdır. Soyut sınıflar, halihazırda uygulanmış üyeleri de sağlayabilir. Bu nedenle, soyut bir sınıfla belirli bir miktarda özdeş işlevsellik sağlayabilirsiniz, ancak bir arabirim ile olamaz.
Bileşenlerinize polimorfizm sağlamak için bir arabirim mi yoksa soyut bir sınıf mı kullanacağınıza karar vermenize yardımcı olacak bazı öneriler.
Kopyalandığı yer:
http://msdn.microsoft.com/en-us/library/scsyfw1d%28v=vs.71%29.aspx
Bu ifadelerden herhangi biri durumunuz için geçerliyse soyut sınıflar kullanmayı düşünün :
Bu ifadelerden herhangi biri durumunuz için geçerliyse arayüzleri kullanmayı düşünün :
Cevaplar diller arasında farklılık gösterir. Örneğin, Java'da bir sınıf birden çok arabirim uygulayabilir (devralma), ancak yalnızca bir soyut sınıftan devralma. Böylece arayüzler size daha fazla esneklik sağlar. Ancak bu C ++ için geçerli değildir.
Benim için, birçok durumda arayüzlerle giderdim. Ancak bazı durumlarda soyut dersleri tercih ederim.
OO'daki sınıflar genellikle uygulamaya atıfta bulunur. Arabirimlerle gittiğim çocuklara bazı uygulama ayrıntılarını zorlamak istediğimde soyut sınıflar kullanıyorum.
Tabii ki, soyut sınıflar sadece uygulamayı zorlamak için değil, aynı zamanda ilgili birçok sınıf arasında bazı detayları paylaşmak için de yararlıdır.
Bazı temel uygulamalar sağlamak için soyut bir sınıf kullanın.
Java'da "sağlamak" işlevi için bir (soyut) sınıftan miras alabilir ve işlevselliği "sağlamak" için birçok arabirim uygulayabilirsiniz
Tamamen miras temelinde, açık bir şekilde torun, soyut ilişkiler (yani hayvan-> kedi) tanımladığınız ve / veya sanal veya kamuya açık olmayan mülklerin, özellikle paylaşılan durumun (Arayüzlerin destekleyemediği durumların mirasına) ihtiyaç duyduğunuz bir Özet kullanırsınız. ).
Yapabileceğiniz kalıtım üzerine kompozisyonu (bağımlılık enjeksiyonu yoluyla) denemeli ve desteklemelisiniz ve sözleşmeler olan arayüzlerin Abstracts'ın yapamayacağı şekilde ünite testini, endişelerin ayrılmasını ve (dil değiştirerek) çoklu kalıtımı desteklediğini unutmayın.
Arayüzlerin soyut sınıflardan daha iyi ücret aldığı ilginç bir yer, bir grup (ilgili veya ilgisiz) nesneye ekstra işlevsellik eklemeniz gerektiğidir. Onlara temel bir soyut sınıf veremezseniz (örneğin, sealed
bir üst öğe veya zaten bir üst öğeye sahipseniz), onlara kukla (boş) bir arabirim verebilir ve ardından bu arabirim için uzantı yöntemleri yazabilirsiniz.
Bu çok zor bir çağrı olabilir ...
Verebileceğim bir işaretçi: Bir nesne çok sayıda arabirim uygulayabilirken, bir nesne yalnızca bir temel sınıfı devralabilirken (c # gibi modern bir OO dilinde, C ++ 'nın çoklu kalıtım olduğunu biliyorum - ama kaşlarını çatmadı mı?)
Soyut bir sınıfın uygulamaları olabilir.
Bir arayüzün uygulamaları yoktur, sadece bir tür sözleşme tanımlar.
Dile bağlı bazı farklılıklar da olabilir: örneğin C # birden fazla mirasa sahip değildir, ancak bir sınıfa birden çok arabirim uygulanabilir.
Temel başparmak kuralı: "İsimler" için Abstract sınıfını ve "Fiiller" arayüzünü kullanın
Örn: car
soyut bir sınıftır ve drive
onu bir arayüz yapabiliriz.
drive
arabadaki işlevselliğini de koyabiliriz - bu soyut bir sınıftır.