Herkese merhaba. Nesne yönelimli programlamanın son konusu olan soyutlama (abstraction) konusunu inceleyeceğiz. Soyutlama projedeki karmaşıklığı azaltmayı sağlar. Birden fazla sınıfa kalıtım verecek olan en temel nesneler soyutlama yöntemiyle oluştulur. Üretimde kullanılmayacak ama diğer tasarımların kalıtım aldığı bir temel kurulur. Ancak kendisi somut olarak proje içerisinde yer almaz. Örneklere github reposundan ulaşabilirsiniz.
Soyutlama (Abstraction)
Nesne yönelimli programlamada problemi çözmek için kullanılan yöntem öncelikle daha genel, daha basit ve soyut olmalıdır. Detayları saklamak ve sadece gösterilmek istenen bilgilieri kullanıcıya göstermek amacıyla kullanılır. Soyutlama hem classlar üzerinden hem de interfaceler üzerinden yapılabilir. Abstract class içerisinde normal class üyelerini barındırabilir. Örnek olarak kalemleri düşünebiliriz. Göz kalemi, tahta kalemi, uçlu kalem hepsi ortak özelliklerini barındıran kalem sınıfından türetilirler. Ancak kalem tek bir somut nesne değildir.
internal abstract class Pen { public string Brand { get; set; } public string Color { get; set; } public abstract string Write(); } internal class BoardPen : Pen { public bool CanRefill { get; set; } public override string Write() { return "I am a board pen!"; } } internal class MechanicalPencil : Pen { public decimal LineWidth { get; set; } public override string Write() { return "I am a MechanicalPencil"; } } internal class EyePen : Pen { public bool DoesHaveBrush { get; set; } public bool IsWaterBased { get; set; } public int Thickness { get; set; } //Kalemin kapağını açmak için kullanılan fonksiyon public void OpenCap() { } public override string Write() { return "I am a EyePen"; } }
Burada kullandığımız override yöntemini daha önce Polymorphism yazısında görmüştük. Abstract metotlar, virtual metotlar gibi ezilme yeteneğine sahiptir. Ancak abstract class içerisinden tıpkı abstract metotlar gibi virtual bir metot oluşturmak istediğizde IDE bizi uyarır. Çünkü, abstract classlar içerisinde soyut olmayan bir metot çağırmak istiyorsak mutlaka bodysini yani gövdesini de yazmamız gerekir. Somut bir class içerisine abstract metotlar kullanılamadığı için somut classlarda virtual metotlar kullanılır. Abstract classlar içerisinde genellikle override edilebilen metotlar yer alır. Somut metotlar da ekleyebiliriz ancak çok tavsiye edilen bir kullanım değildir.
internal abstract class Pen { public string Brand { get; set; } public string Color { get; set; } public abstract string Write(); public virtual string Erase() { return "Erase"; } }
Main program içerisinde önce abstract class üzerinden nesne oluşturmayı denersek IDE bizi uyarır. Abstract class’lardan instance oluşturulamaz. Çünkü, içerisinde abstract ve implementasyonu bulunmayan üyeler barındırabilir. Pen abstract sınıfındaki Write metodu buna bir örnek teşkil eder.
static void Main(string[] args) { //Pen pen = new Pen(); EyePen eyePen = new EyePen(); eyePen.Brand = "Flormar"; eyePen.Thickness = 1; eyePen.Color = "Black"; eyePen.DoesHaveBrush = false; eyePen.IsWaterBased = true; Console.WriteLine(eyePen.Write()); BoardPen boardPen = new BoardPen(); boardPen.Brand = "Globox"; boardPen.CanRefill = true; boardPen.Color = "Red"; Console.WriteLine(boardPen.Write()); Console.ReadLine(); }
Inheritance alarak ürettiğimiz classların nesnelerini ürettiğimizde her birisinin kendine ait Write metodunu çağırdığını görüyoruz. Bu sayede ortak özelliklere sahip ancak farklı yetenekleri olan sınıfları bu şekilde üretebiliriz. Türetilen nesnelerin büyük bir çoğunluğu bir işi aynı yapıyorsa farklı yapan nesneler için virtual metot oluşturabiliriz.
Abstract nesneleri, onlardan türetilen nesnelerle birlikte main program içerisinden çağırabiliriz. Bu durumda türetilen nesnenin override metodu çalışır. Ancak türetilen nesnede bulunan özellikler ya da metotlar bu nesnede yer almaz.
static void Main(string[] args) { Pen pen = new MechanicalPencil(); pen.Brand = "Faber Castell"; pen.Color = "Pink"; Console.WriteLine(pen.Write()); Console.ReadLine(); }
İç İçe Abstract Class
Abstract class başka bir abstract classa kalıtım ile geçiyorsa base sınıftaki soyut metotları override etmeye zorlamaz. Örnek üzerinden ilerlersek daha iyi anlaşılacağını düşünüyorum. Enstrümanları düşünelim. Çok fazla çeşitte enstrüman bulunmasına rağmen somut tek bir enstrüman bulunmuyor. Örnek enstrüman olarak gitarı düşünelim. Gitar da entstrüman gibi çok fazla çeşidi bulunmasına rağmen somut bir nesne değildir. O zaman 2 adet abstract class oluşturmamız gerekiyor.
internal abstract class Instrument { public abstract string PlaySound(); } internal abstract class Guitar : Instrument { public abstract int StringCount { get; } }
Gitar sınıfı içerisinde PlaySound metodunu override etmediğimiz için türetilen diğer classlarda aşağıdaki gibi bir hata göreceğiz.
Gitar sınıfı içerisinde override işlemi yaparsak PlaySound metodunu ezme zorunluluğu ortadan kalkar. Daha önce de bahsettiğim gibi büyük çoğunluk ortak bir işlemi yürütüyorsa bu yöntemi kullanabiliriz.
internal class BassGuitar : Guitar { public override int StringCount => 4; public override string PlaySound() { return "I am a Bass Guitar!"; } } internal class ClassicalGuitar : Guitar { public override int StringCount { get => 6; } public override string PlaySound() { return "I am a Classical Guitar!"; } } internal class ElectricGuitar : Guitar { public override int StringCount => 6; public override string PlaySound() { return "I am a Electric Guitar!"; } }
Burada ürettiğimiz sınıflar doğrudan enstrüman sınıfından kalıtım almasa da gitar sınıfı üzerinden kalıtım aldığından PlaySound metoduna erişim sağlamaktalar.
Abstraction ile ilgili bildiklerimi bir araya getirmeye çalıştım. Bir sonraki yazıya kadar kendinize çok iyi bakın! 😊
Bir yanıt yazın