Kaynak : http://jasonroell.com/2014/12/09/interfaces-vs-abstract-classes-what-should-you-use/
C #, son 14 yılda olgunlaşan ve gelişen harika bir dildir. Bu, geliştiriciler için harika bir şey çünkü olgun bir dil, elimizde olan çok sayıda dil özelliği sağlıyor.
Bununla birlikte, çok fazla güçle çok sorumluluk olur. Bu özelliklerden bazıları yanlış kullanılabilir veya bazen bir özelliği neden diğerine tercih edeceğinizi anlamak zordur. Yıllar boyunca, birçok geliştiricinin mücadele ettiği bir özellik, ne zaman bir arayüz kullanmayı veya soyut bir sınıf kullanmayı seçmektir. Her ikisinin de avantajları ve dezavantajları ve her birini kullanmak için doğru zaman ve yer var. Ama nasıl karar veririz ???
Her ikisi de türler arasında ortak işlevselliğin yeniden kullanılmasını sağlar. En belirgin fark arayüzlerin işlevsellikleri için herhangi bir uygulama sağlamaması, soyut sınıflar ise bazı “temel” veya “varsayılan” davranışları uygulamanıza ve daha sonra bu varsayılan davranışı gerekirse türetilmiş sınıflarla “geçersiz kılma” özelliğine sahip olmasıdır. .
Bu iyi ve iyidir ve kodun büyük ölçüde yeniden kullanılmasını sağlar ve DRY (Kendinizi Tekrar Etmeyin) yazılım geliştirme prensibine bağlıdır. Soyut sınıflar, bir “is” ilişkiniz olduğunda kullanmak için harikadır.
Örneğin: Bir altın av köpeği “tür” bir köpek türüdür. Bir fino köpeği de öyle. Her ikisi de, tüm köpekler gibi havlayabilir. Bununla birlikte, kaniş parkının “varsayılan” köpek kabuğundan önemli ölçüde farklı olduğunu belirtmek isteyebilirsiniz. Bu nedenle, aşağıdaki gibi bir şey uygulamanız mantıklı olabilir:
public abstract class Dog
{
public virtual void Bark()
{
Console.WriteLine("Base Class implementation of Bark");
}
}
public class GoldenRetriever : Dog
{
// the Bark method is inherited from the Dog class
}
public class Poodle : Dog
{
// here we are overriding the base functionality of Bark with our new implementation
// specific to the Poodle class
public override void Bark()
{
Console.WriteLine("Poodle's implementation of Bark");
}
}
// Add a list of dogs to a collection and call the bark method.
void Main()
{
var poodle = new Poodle();
var goldenRetriever = new GoldenRetriever();
var dogs = new List<Dog>();
dogs.Add(poodle);
dogs.Add(goldenRetriever);
foreach (var dog in dogs)
{
dog.Bark();
}
}
// Output will be:
// Poodle's implementation of Bark
// Base Class implementation of Bark
//
Gördüğünüz gibi, bu, kodunuzu KURU tutmanın ve türlerden herhangi birinin özel bir vaka uygulaması yerine varsayılan Bark'a güvenebilmesi durumunda temel sınıf uygulamasının çağrılmasına izin vermenin harika bir yolu olacaktır. GoldenRetriever, Boxer, Lab gibi sınıfların tümü, yalnızca Köpek soyut sınıfını uyguladıkları için “varsayılan” (bas sınıfı) Bark'ı ücretsiz olarak devralabilir.
Ama eminim zaten biliyordun.
Buradasınız, çünkü soyut bir sınıf üzerinden neden bir arayüz seçmek isteyebileceğinizi veya tam tersini anlamak isteyebilirsiniz. Soyut bir sınıf üzerinde bir arabirim seçmek isteyebileceğiniz nedenlerden biri, varsayılan bir uygulamaya sahip olmamanız veya bunu önlemek istemenizdir. Bunun nedeni genellikle arabirimi uygulayan türlerin “ilişkisiz” bir ilişkidir. Aslında, her türün bir şey yapabilmesi ya da bir şey yapabilmesi için “yapabilmesi” ya da “bereketine” sahip olması dışında hiçbir şekilde ilişkili olmaları gerekmez.
Şimdi bu ne anlama geliyor? Mesela: Bir insan ördek değil… ve ördek insan değil. Çok belirgin. Ancak, hem bir ördek hem de bir insan yüzme yeteneğine sahiptir (insan yüzme derslerini 1. sınıfta geçtiği göz önüne alındığında :)). Ayrıca, bir ördek bir insan olmadığından ya da tam tersi olduğundan, bu bir “bir” ilişkidir, bunun yerine “mümkün” bir ilişkidir ve bunu göstermek için bir arayüz kullanabiliriz:
// Create ISwimable interface
public interface ISwimable
{
public void Swim();
}
// Have Human implement ISwimable Interface
public class Human : ISwimable
public void Swim()
{
//Human's implementation of Swim
Console.WriteLine("I'm a human swimming!");
}
// Have Duck implement ISwimable interface
public class Duck: ISwimable
{
public void Swim()
{
// Duck's implementation of Swim
Console.WriteLine("Quack! Quack! I'm a Duck swimming!")
}
}
//Now they can both be used in places where you just need an object that has the ability "to swim"
public void ShowHowYouSwim(ISwimable somethingThatCanSwim)
{
somethingThatCanSwim.Swim();
}
public void Main()
{
var human = new Human();
var duck = new Duck();
var listOfThingsThatCanSwim = new List<ISwimable>();
listOfThingsThatCanSwim.Add(duck);
listOfThingsThatCanSwim.Add(human);
foreach (var something in listOfThingsThatCanSwim)
{
ShowHowYouSwim(something);
}
}
// So at runtime the correct implementation of something.Swim() will be called
// Output:
// Quack! Quack! I'm a Duck swimming!
// I'm a human swimming!
Yukarıdaki kod gibi arayüzler kullanmak, bir nesneyi bir şeyleri “yapabilen” bir yönteme geçirmenize izin verecektir. Kod nasıl yaptığını umursamıyor… Tek bildiği şey, o nesne üzerindeki Swim yöntemini çağırabilmesidir ve bu nesne, türüne göre çalışma zamanında hangi davranışın alınacağını bilecektir.
Bir kez daha, bu kodunuzun KURU kalmasına yardımcı olur, böylece nesneyi aynı çekirdek işlevini (ShowHowHumanSwims (insan), ShowHowDuckSwims (ördek) vb.) Oluşturmak için çağıran birden çok yöntem yazmak zorunda kalmazsınız.
Burada bir arabirim kullanmak, çağrı yöntemlerinin hangi türün ne olduğu veya davranışın nasıl uygulandığı konusunda endişelenmesine gerek yoktur. Sadece arayüz verildiğinde, her nesnenin Swim yöntemini uygulamış olması gerektiğini bilir, böylece kendi kodunda çağırmak ve Swim yönteminin davranışının kendi sınıfında işlenmesine izin vermek güvenlidir.
Özet:
Bu yüzden temel kuralım, bir sınıf hiyerarşisi veya / için “varsayılan” bir işlevsellik uygulamak istediğinizde soyut bir sınıf kullanmaktır ve / ve birlikte çalıştığınız sınıf veya türler bir “ilişkidir” (ör. Kaniş “ "Köpek türü).
Öte yandan, “bir” ilişkiniz olmadığında ancak bir şey yapma ya da bir şeye sahip olma “yeteneğini” paylaşan türlere sahip olduğunuzda bir arayüz kullanın (ör. Ördek “insan değildir”, ancak ördek ve insan payı "Yüzme yeteneği".
Soyut sınıflar ve arayüzler arasında dikkat edilmesi gereken bir diğer fark, bir sınıfın birden fazla arayüze uygulayabilmesidir, ancak bir sınıf yalnızca BİR soyut sınıftan (veya bu konudaki herhangi bir sınıftan) miras alabilir. Evet, sınıfları iç içe geçirebilir ve kalıtım hiyerarşisine sahip olabilirsiniz (birçok programın sahip olduğu ve sahip olması gereken), ancak türetilmiş bir sınıf tanımında iki sınıfı devralamazsınız (bu kural C # için geçerlidir. Diğer bazı dillerde genellikle bunu yapabilirsiniz sadece bu dillerdeki arayüzlerin eksikliği nedeniyle).
Ayrıca, Arabirim Ayrışma İlkesine (ISS) uymak için arabirimleri kullanırken de unutmayın. ISP, hiçbir müşterinin kullanmadığı yöntemlere bağımlı olmaya zorlanmaması gerektiğini belirtir. Bu nedenle arayüzler belirli görevlere odaklanmalıdır ve genellikle çok küçüktür (örn. IDisposable, IComparable).
Başka bir ipucu, küçük, özlü işlevsellik parçaları geliştiriyorsanız, arabirimler kullanın. Büyük fonksiyonel birimler tasarlıyorsanız, soyut bir sınıf kullanın.
Umarım bu bazı insanlar için işleri temizler!
Ayrıca daha iyi örnekler düşünebilir veya bir şeyi belirtmek isterseniz, lütfen aşağıdaki yorumlarda belirtin!