k : d kz/ ed wzk'z dd/e' d d >> z7 -...

13
OBJECT ORIENTED PROGRAMMING TEMELLERİ Geçenlerde kitaplığımı kurcalarken eskimiş bir not defteri buldum, yıllar önce (sanırım 2008-2010) OOP öğrenirken aldığım kısa notlarla karşılaştım, yazarak öğrenme metodunu kullanan biri olarak o zaman aldığım notlar nesne yönelimli programlama temelleri konusunda şimdi hâlâ geçerliliğini koruyor. Notlarımı derleyerek, imlâ hatalarından arındırarak ve bu günün yansımalarından birkaç not ekleyerek burada paylaşıyorum, umarım bu notlar faydalı olur. Yazılım kitaplarında nesne yönelimli programlama başlığı altında bu konu anlatılıp, ‘nesne’ kavramı açıklanırken, Okul-Sınıf-Öğrenci örneği verilir. Öğrenci bir nesne ve öğrenciye erişmek için, önce okula sonra sınıfa sonra da öğrenciye (nesneye) erişilir. Daha önce, düşük seviyeli diller ile kod yazmayan, ilk öğrendiği dil C# gibi temeli nesneye dayanan bir dil ile uğraşan birisi, bu örnek sonrasında “- e başka türlü nasıl olabilirdi ki zaten?” gibi bir soruyu aklına getirebilir. Nesne yönelimli olmayan bir dil yazmış olan geliştiriciler, nesne yönelimli programlamanın ne ifade ettiği, sunduğu kolaylıkları, neden ihtiyaç duyulduğu gibi konuları kavrama konusunda avantajlıdırlar, bu nedenle kavranması açısından biraz geriye gidip nasıl aşamalardan geçildiğine değinmekte fayda var.

Upload: dinhtu

Post on 18-Feb-2019

222 views

Category:

Documents


0 download

TRANSCRIPT

OBJECT ORIENTED PROGRAMMING TEMELLERİ

Geçenlerde kitaplığımı kurcalarken eskimiş bir not defteri buldum, yıllar önce (sanırım 2008-2010) OOP öğrenirken aldığım kısa notlarla karşılaştım, yazarak öğrenme metodunu kullanan biri olarak o zaman aldığım notlar nesne yönelimli programlama temelleri konusunda şimdi hâlâ geçerliliğini koruyor. Notlarımı derleyerek, imlâ hatalarından arındırarak ve bu günün yansımalarından birkaç not ekleyerek burada paylaşıyorum, umarım bu notlar faydalı olur. Yazılım kitaplarında nesne yönelimli programlama başlığı altında bu konu anlatılıp, ‘nesne’ kavramı açıklanırken, Okul-Sınıf-Öğrenci örneği verilir. Öğrenci bir nesne ve öğrenciye erişmek için, önce okula sonra sınıfa sonra da öğrenciye (nesneye) erişilir. Daha önce, düşük seviyeli diller ile kod yazmayan, ilk öğrendiği dil C# gibi temeli nesneye dayanan bir dil ile uğraşan birisi, bu örnek sonrasında “- e başka türlü nasıl olabilirdi ki zaten?” gibi bir soruyu aklına getirebilir. Nesne yönelimli olmayan bir dil yazmış olan geliştiriciler, nesne yönelimli programlamanın ne ifade ettiği, sunduğu kolaylıkları, neden ihtiyaç duyulduğu gibi konuları kavrama konusunda avantajlıdırlar, bu nedenle kavranması açısından biraz geriye gidip nasıl aşamalardan geçildiğine değinmekte fayda var.

OOP öncesinde işler nasıl yürüyordu? Programlama dillerinin evrimi derin bir konu, bu nedenle 0-1’lere, instruction pointer’lara kadar derinlere inmemize gerek yok. Kısaca özetleyecek olursak programlama dili kavramının ortaya çıktığı uzun yıllar öncesinden yana bu evrimi; doğrusal programlama, yapısal programlama ve nesne tabanlı programlama olarak üç başlık altında inceleyebiliriz. Doğrusal programlama, alt alta yazılan ve çoğu zaman aynı satırlardan oluşan, start, goto, end gibi ifadelerin kullanıldığı, program büyüdükçe karmaşıklaşan ve geliştirilmesi yeniden yazılması kadar bir zaman gerektiren bir yapısı olduğundan yapısal programlama kavramı geliştirilmiştir. Yapısal programlama da kodlar işlevselliklerine göre mantıksal olarak parçalara bölünebilirler ve bu sayede uzun ve karmaşık programların küçük parçalar haline getirilip yazılmasını mümkün kılmaktadır. Bu yapı üzerinde çalışmak için metotlar kullanılırken nesneye yönelimli programlamada ise sınıflar içinde metot yapısını veri yapısı ile ilişkilendirilir, nesneler her biri kendi içlerinde veri işleyebilir ve diğer nesneler ile çift yönlü veri transferinde bulunabilir, yapısal programlamada ise programlar sadece bir komut dizisi olarak görülebilmektedir. Bu nesne yönelimli programlamayı diğer yapılardan öne çıkaran en önemli özelliklerden biridir. Yüz binlerce satır kod nesne yönelimli bir dil ile yazıldığında yönetilmesi daha kolay olacaktır, çünkü kendi içerisinde yüzlerce nesneye bölünecektir. Amaç geliştirilen programın mümkün olduğu kadar küçük ve anlamlı parçalara bölüp, her parçayı bir türde nesne haline getirip daha sonra bu nesneleri gerektiği yerde kullanmaktır. Özet olarak oop reusable kodların yazılması, bu da yazılan programın esnetilebilmesi ve geliştirilebilmesinde büyük kolaylık sağlamaktadır. C++ programlama dilinin yazarı Bjarne Stroustrup’a göre bir dilin kendisini nesne yönelimli olarak tanımlayabilmesi için, üç kavramı desteklemesi gerekir: nesneler, sınıflar ve kalıtım. Ancak nesne yönelimli diller; Encapsulation (sarmalama), Inheritance (Miras Alma) ve Polymorphism (Çok Biçimlilik) üçlüsü üzerine oturtulmuş diller olarak düşünülür. Tanımlamadaki bu değişimin nedeni, geçen yıllar boyunca encapsulation ve polymorphism’in class ve inheritance gibi nesne yönelimli sistemler oluşturmanın bir iç parçası olduğunun farkına varılmasıdır. Sahi, başından beri nesne yönelimli programlama demişken nedir bu Nesne? Her şey nesnedir! Bu felsefi bir yaklaşım gibi oldu kabul ediyorum, bunu biraz açıklayalım. Her şey bir nesnedir demiştim, etrafınıza bir bakın, ilk gözünüze çarpan şey nedir? Ben bu makaleyi siyah 0.5 uç kullanan bir kalemle yazıyorum, bu kalemin bir ağırlığı, bir rengi, bir markası var, bunlar bu kelemin özellikleri. Aynı zamanda bu kalemin tepesindeki metal kapağa basıp içindeki ucu çıkartabiliyorum; bu da davranışı. Nesneler iki temel bileşenden oluşur. Bunlar özellik (property) ve davranış (behavior)’dır. Burada kalem bir nesnedir. “İşe .net tarafından bakınca, C# nesne yönelimli bir dildir ve bu dili kullananlar; “tanımladığımız her şey System.Object’ten türediğine göre her şey bir object(nesne)dir” diyebilir. .net tarafından bakılınca bu manaya da gelebilir ama bu sadece bir kavramdır, yani nesnenin tanımı değildir .”

Bu konuştuklarımızın kod karşılığını görelim. Nesneleri yazmak için önce bir şablon(kalıp) oluşturur, daha sonra bu şablondan istediğimiz kadar nesneyi çıkartabiliriz. Bu şablonlara class, bu şablonlardan oluşturulan nesnelere’de object adı verilir. Class ile object arasındaki ilişki bu şekildedir. class Kalem { string Renk; string Marka; string Uc; int Uzunluk; int Agirlik; void Bas() { // üzerine basılınca bir miktar uç çıkar } } Yukarıdaki kod örneğinde görüldüğü gibi, sınıf nesneyi tanımlayan bir veri türü, nesne ise sınıftan türetilebilen bir yapıdır. Nesneleri yazmak için önce bir şablon oluşturur, sonra bu şablondan istediğimiz kadar nesneyi çıkartabiliriz. Kalem k = new Kalem(); Yukarıdaki tek satırlık kodda new Kalem(); diyerek kalem sınıfının instance’ını almış yani Kalem classından nesne türetmiş olduk. Peki, instance alınca ne oluyor? Burada Ram yapısına göz atmakta fayda var. Ram kısaca, stack ve heap olan iki alandan oluşur. Stack, local variable’ların olduğu, heap ise class instance’larının olduğu alandır. Kalem sınıfını instance alıp, değer atamak gibi birkaç işlem yapıp, bu işlemlerin ram’e nasıl yansıdığını göstermek için basit bir stack-heap schema hazırladım. (Bu dökümanın sonunda yer almaktadır) Eğer linkteki pdf’i incelediyseniz gördüğümüz gibi Kalem sınıfı k ile tanımladık (Kalem k;) ama türetmedik; türetmediğimiz için ram’de stack alanına olarak atandı, k değeri Kalem sınıfına ulaşmak için bir değerdir. Kalem bilgisinin tutulabilmesi için Heap bölümünde tanımlanması (instance alınması) gereklidir. new Kalem() diyerek instance alıp ramdeki değişikliğe baktığımızda kalem nesnesinin bir adres ile ilişkilendirildiğini görüyoruz. Bu adres bizim için referans adresi, yani biz kalem nesnesini call ettiğimiz zaman bu adresten çağırılır, steak alanında, biz yeni bir instance almadan, yeni bir kalem nesnesi oluşturmaz. Steak heap yapısı detaylı bir konu; bu nedenle “Memory management” başlığı altında bu konuyu araştırmanızı öneririm.

Buraya kadar nesneleri tanımladık fakat tanımlanan nesne ile ilgili bir erişim sorunumuz var: Acces Modifiers (Erişim Belirleyiciler) Yazdığımız program içerisindeki classlara, bu classlar içindeki metodlara, değişkenlere nasıl ve ne zaman erişeceğimizi belirten kurallar var, bunlara Acces Modifiers (Erişim Belirleyiciler) denir. Bu kurallar nesne içerisindeki üyelerin erişim durumlarını belirler. Bunlar : public, private, protected, internal, protected internal ’dır. Şimdi bunları inceleyelim. public : Public olarak tanımlanan üye tüm erişimlere açıktır. protected : Kendi üyesi olduğu (tanımlandığı) sınıflardan ve bu sınıftan türetilmiş sınıflardan erişilir. private : Yalnızca üyesi olduğu sınıftan erişilir. internal : Aynı program(assembly) içerisinden erişilir. protected internal : Class’ın içinden ve ondan türetilen sınıfların içinden erişilir. Nesneyi anlatırken yazdığımız Kalem sınıfı içerisindeki değişkenlerin bir erişim belirleyicisi olmadığını fark etmişsinizdir, yazılan her hangi bir nesne için erişim belirlenmemiş ise, bu nesnenin erişimi default olarak private‘dır. Aşağıdaki tabloda yapıların erişilebilirlik durumunu görebiliriz. Yapı Adı Default Erişilebilirlik Geçerli olabilen erişilebilirlik Üyelerin erişebilirliği Enum İnternal internal public private protected

Public protected internal private protected internal Struct İnternal İnternal Public Private (nested struct)

Public İnteral private Interface Public Public internal

Constructor (Yapıcı) Metot Bir sınıftan bir nesne oluşturulduğu zaman, yani nesneyi ram’de inşa ederken başlangıç olarak o nesne ile ilgili yapısını değiştirecek bazı farklılıklar olsun isteyebiliriz, bunun için constructor adı verilen özel bir metodumuz var. Aslında bir nesneyi türetirken new anahtar sözcüğünü kullandığımızda default olarak bir constructor oluşturmuş oluruz, eğer kendi constructor’ımızı oluşturmaz sak default olarak bu temel alınacaktır. (variable initilializer) Constructor sınıf adı ile aynı adı taşır, dışarıdan parametre alabilirler, geriye değer döndürmezler, overload yapılabilirler. (overload nedir? gibi bir soru var ise kafanızda, yazının ilerleyen bölümlerinde öğreneceksiniz) Şimdi Kalem sınıfında çalışarak bir constructor yazalım ve yazdığımız kodu yorumlayalım. public class Kalem { public Kalem() { Marka = "Faber Castell"; } public Kalem(string marka) { Marka = marka; } private string Renk; private string Marka; private string Uc; private int Uzunluk; private int Agirlik; private void Bas() { // üzerine basılınca biraz uç çıkar } } Kod Yorumu: Kalem adlı sınıfımızda bir constructor oluşturuyoruz, constructor sınıf adı ile aynı adı taşır, birden fazla constroctor olabilir, bu kod bloğunda iki tane constructors var, ilki default olarak markamızı tanımlayan, “Faber Castell” değerini verendir, biz bu nesneyi üretirken, marka değişkenine otomatik olarak “Faber Castell” değeri atanır. Nesneyi her ürettiğimiz yerde tekrar bu marka değişkenine değer atamak zorunda kalmayız. İstisnai durumlarda ise overload yapılabilsin diye, ikinci construcor parametre alarak marka değişkenine değer atanması sağlanır. int, byte, char gibi değer tiplerinin de yapıcı yordamları bulunmaktadır, kod yazarken bu değerlere tiplerine ilk değerlerini vermeden kullanmak istediğimizde hata alınır.

Destructor (Yıkıcı) Metot Oluşturduğumuz nesnenin, bellekten silinmeden önceki gerçekleştireceği son işlemi gerçekleştiren metoddur. Tilde (~) işareti ile başlar. public class Kalem { ~Kalem() { // bellekten silinmeden önceki gerçekleştirilecek işlemler } } Encapsulation (Sarmalama) Encapsulation adı verilen yapı, bir sınıf içerisindeki değişkenlere “kontrollü bir şekilde erişimi sağlamak / denetlemek” için kullanılan bir yapıdır. Class içerisindeki değişken privete yapılırak dışarıdan direkt erişilmesi engellenir, bu değişken içerisine değer atıp değer okumak için get ve set adı verilen metodlar kullanılır. Yani direkt değişkene değil, bu değişkene erişmek (işlem yapmak) için bu metodlar ile çalışılır. set değeri alıp değişkene atar, get’de değeri geri döndürür, tabii bu işlem sizin belirlediğiniz, olması gereken kurallar çerçevesi içinde gerçekleşir. Basit bir örnek verecek olursak; kitap adında bir class’ınız var ve bu class’ın içinde yazar adı, yayın evi, kitap sayfası, baskı yılı vb. property’ler mevcut. Bu property’lerden kitap sayfasına, her kitabın 2’den fazla sayfası olmak zorunda olduğu için 2 sayısından az bir değer girdirmemeliyiz, buna teşebbüs edildiğinde de hata döndürmeliyiz, bu nedenle sayfa sayısı propertysini encapsulete edelim. (Property de nedir? En basit hali ile public int MyProperty { get; set; } şeklindeki yapıdır) public class Kitap { public string yazarAdi { get; set; } private int _sayfaSayisi; public int sayfaSayisi { get { return _sayfaSayisi; } set { if (value <= 2) { throw new Exception("Kitap Sayısı 2'den büyük olmalıdır."); } _sayfaSayisi = value; } } public string yayınEviAdi { get; set; } public DateTime basimTarihi { get; set; } }

Kod Yorumu : Sayfa sayısı property’sini 2’den küçük ve 2 sayısına eşit olduğunda “Kitap Sayısı 2'den büyük olmalıdır." Şeklinde bir hata dönecektir. Kitap class’ı içerisindeki SayfaSayisi propertysini encapsulate etmiş olduk. Çok basit değil mi? Not: c# dilinin v6 sürümünde auto property initilalizers özelliği gelerek property yapısı biraz daha geliştirilmiştir public string Key { get; } = "ABC123"; public int ID { get; set; } = 17; set değerini kaldırarak Key propertysi sürekli “ABC123” değerini döndürecektir. Ve readonly’dir. Inheritance (Miras Alma, Kalıtım) Miras kelimesini tanımı olarak, en basit haliyle birinden başka birine kalan varlık anlamına geldiğini biliyoruz, bunun programlama tarafındaki anlamı ise; bir class içersindeki elemanların (property, metod) farklı nesneler tarafından kullanılabilmesini sağlayan yapıdır. Yine yayın evi örneğinden gidecek olursak; bir yayın evi sadece kitap değil, farklı türlerde baskılı ürün (aylık dergi, broşür) yayınlayabilir, bunların ortak özelliklerinden biri de baskı tarihinin olmasıdır. Yukarıda Kitap sınıfını düşünelim, yazarAdi, yayinEvi, sayfaSayisi ve basimTarihi olarak propertyleri var, kitap classında basimTarihi olduğu gibi, benim brosur adlı bir nesnem olduğunda da basimTarihi’ne, dergi adlı bir nesnem olduğunda da basimTarihi’ne ihtiyac duyacağım, bu nedenle türeteceğim nesnede bu property’leri tek tek yazmaktansa, bir tane class’ım olsun(base class) , bu class’ın içinde benim oluşturacağım her class’ta ihtiyacım olan propertyler olsun, ve ben oluşturduğum bu class’ları (derived class), bu sınıf ile yani base class ile ilişkilendireyim. Bu yazdıklarımızı kod haline dönüştürüp yorumlayalım.

// Base Class’ımız public class YayinEvi { public string yayınEviAdi { get; set; } public DateTime basimTarihi { get; set; } } // Base Class’tan inheritance almış Kitap sınıfımız public class Kitap : YayinEvi { public string yazarAdi { get; set; } private int _sayfaSayisi; public int SayfaSayisi { get { return _sayfaSayisi; } set { if (value <= 1) { throw new Exception("Kitap Sayısı 1'den büyük olmalıdır."); } _sayfaSayisi = value; } } }

Kitap ki = new Kitap(); ki.yayınEviAdi = "Everest Yayınları"; ki.basimTarihi = new DateTime(2009, 06, 01); ki.SayfaSayisi = 367; ki.yazarAdi = "Alev Alatlı"; ki.kitapAdi = "HoolyWood'u kapattığım gün";

Kod Yorumu: Kitap sınıfının içerisinde yayınEviAdi ve basimTarihi adlı propertyler olmamasına rağmen, bu sınıfı türettiğimizde, bu propertylere erişebildiğimizi görüyoruz, çünkü Kitap sınıfı, YayınEvi sınıfından miras aldığı için ve bu sınıfın elamanlarına erişebilmektedir. class Kitap : YayinEvi { } c# dilinde multiple inheritance yoktur, sadece bir sınıftan miras alabilirsiniz.

Abstract (Soyutlama) Inheritance olayını öğrendiğimize göre, olayı biraz daha ileriye götürelim. Soyut kavram, sözlük tanımı olarak; nesnelerin oluş tarzını ifade eden kavramlara soyut kavram denir. Bir restorana gittiğimiz zaman, bizimle ilgilenen garsona “bize biraz besin getirebilir misin?” diyebilir miyiz? Aklıselim bir insan isek cevabımız hayır olacaktır. Biraz besin yerine “zeytinyağlı enginar” dersek daha doğru olacaktır, çünkü besin soyut bir kavramdır, zeytinyağlı enginar ise besin sınıfına ait bir nesnedir. Bu örneğe kod tarafından yaklaşalım; kendisinden nesne üretmeye izin vermeyen, sadece miras alınabilen sınıfları abstract class olarak tanımlayabiliriz. Abstract classın içerisine, gövdesi olan abstract bir metod yazdığınızda ' cannot declare a body because it is marked abstract hatası alırsınız, çünkü abstract metodların gövdesi olmaz. Yine bu metodu private olarak tanımladığınızda virtual or abstract members cannot be private hatası alırsınız, yani hata metninden de anlaşılacağı üzere abstract metodlar private olamaz. Abstract base class mantığındadır, sadece temel oluşturmak için kullanılır, yani bu sınıf inheritance alındıktan sonra kullanılabilir, tek başına işlevi yoktur, bu nedenle en karakteristik özelliği kendisinden nesne üretmesine izin vermez, abstract olmayan bir sınıfın içerisinde abstract bir elaman olamaz. Abstract bir sınıfı, miras alan bir sınıf, bu miras aldığı abstract sınıfı içerisindeki metodu override eder. Kod tarafına göz atalım: public abstract class KitapBase { public abstract int SayfaSayisi { get; set; } } // KitapBase sınıfını inheritance almış Kitap sınıfımız. public class Kitap : KitapBase { private int _sayfasayisi; public override int SayfaSayisi { get { return _sayfasayisi; } set { _sayfasayisi = value; } } } Kod yorumu: Kitap sınıfı, base class olan KitapBase sınıfını miras alıyor ve SayfaSayisi property’sini override ediyor.

Polymorphism (Çok Biçimlilik) Polymorphism; bir sınıftan miras alınan metodun, miras alındığı sınıftaki işlevi ile, miras alan sınıfta farklı işlevlerle çalıştırılabilme özelliğidir. Bu işlemin nasıl olduğunu görmeden önce bilmemiz gereken bazı kavramlar var: virtual : Metodun türetilmiş bir sınıfta geçersiz kılınmasını sağlamak için kullanılır. override : Inheritance alınan bir sınıftaki metodu tekrar yazarak, base class’takini ezmiş, yoksaymış yani override etmiş oluruz. Bunun gerçekleşebilmesi için base class’taki metodun virtual olarak tanımlanması gerekir, yani buna izin vermesi gerekir ki virtual’un tanımını bu şekilde yapmıştık. overload : Türkçe karşılığı ile bir metodun aşırı yüklenmesidir, yani aynı isimde birden fazla metod farklı parametreler alabilir, ya da almayabilir. Hatırlarsanız Constructor’ı anlatırken Kalem sınıfı örneği vermiştik, Kalem sınıfında iki tane constuctor vardı, biri sadece sadece Marka Değişkenini set ediyor, diğeri de dışarıdan parametre alıyordu. Yani iki farklı şekilde işleyişi vardı, yani overload edilebiliyordu. base : türetilmiş sınıfın içerisinden, türetildiği sınıftaki base class’taki (public, internal, protected olarak tanımlanmış) elemanlara erişmek için kullanırız. Özetle Base class’ı referans alır. (abstract olarak tanımlanmış üyeler için kullanılamaz) base sınıfı bir üst sınıfı temsil eder. grandparent-parent-child gibi bir yapıyı düşünelim, grandparent’ta tanımlanış bir metoda child üzerinden this ile erişemeyiz. Sadece bir üst sınıfa, yani türetildiği sınıfa erişilebilir. this : bu sözcüğün base’den farkı içinde bulunduğu nesneyi referans alır. Bu anlattıklarımızı pekiştirmek için basit bir kod bloğu yazalım. // Base classımız class Base { public virtual void Yaz1() { Console.WriteLine("Yaz1 base olan sınıftan, virtual olarak geldim"); } public virtual void Yaz2() { Console.WriteLine("Yaz2 base olan sınıftan, virtual olarak geldim"); } public void Yaz() { this.Yaz1(); this.Yaz2(); } } // Base class’tan türetilmiş Print classımız class Print : Base { public override void Yaz1() { Console.WriteLine("derived olan sınıftan, override olarak geldim"); } }

------------------------------------------------------------------------- private static void Main() { Print prt = new Print(); prt.Yaz(); Console.ReadLine(); } Kodun çıktısı aşağıdaki gibi olacaktır: derived olan sınıftan override olarak geldim Yaz2 base olan sınıftan, virtual olarak geldim Kod Yorumu: Kodun çıktısından da gördüğümüz gibi this sözcüğünün kullandık ve metodu çağırdık, var ise türetmiş sınıftan override edilmiş olanı, yoksa temel sınıftan asıl halini getirdi. Şimdi öğrendiğimiz bu yeni bilgiler dâhilinde polymorphisme dönelim. Bir sınıftan miras alınan metodun, (bu temel bir sınıf olabilir), bu metodun temel sınıftaki işlevi ile, miras alan sınıfta farklı işlevlerle çalıştırılabilme özelliğidir demiştik, şöyle bir senaryomuz olsun, çeşitli ürünler satan online bir dükkanınız var, Türkiye’nin her yerine sabit bir tutar ile kargo gönderiyorsunuz, fakat sadece taşıma esnasında kırılabilmesi muhtemel ürünlerinizi (cam vazo gibi) korumalı kargo kutusunda gönderiyorsunuz, bu da kargo fiyatını farklılaştırıyor. Her ürün için farklı bir metod yazmak bir çözüm ama OOP ilkelerine ters bir çözümdür, peki bunu polymorphism ile nasıl yaparız bunu kod üzerinde görelim. // Base Classımız public abstract class Transfer { public string KargoFirması { get; set; } public string TeslimSuresi { get; set; } public virtual double KargoUcreti() { return 10.00; } } public class OzelUrun:Transfer { public string UrunAdi { get; set; } public override double KargoUcreti() { return 15.00; } }

Kod Yorumu : OzelUrun adlı classımız, base class olan Transfer classından miras alıyor, buraya kadar her şey anlaşılır durumda, fakat burada farklı bir durum var, iki class içinde de aynı isimli metod var biri virtual diğeride override olarak tanımlanmış. Base classımızdaki KargoUcreti() metodu her ürün için geçerli olup sadece özel ürünlerde farklı bir şekilde çalışması gerekiyordu. Bunun için baseclass’ımızdaki KargoUcreti() metodunu override ederek içeriğini değiştirdik, böylece OzelUrun adlı classımızdaki KargoUcreti() metodu, bizim tanımladığımız şekilde çalışacaktır, base classımızdaki metod normal ürünler için 10.00 değerini döndürürken, biz base class’taki KargoUcreti() metodunu override ederek içeriğini değiştirdik ve bizim belirlediğimiz 15.00 değerini döndürmesini sağladık. Dikkat ederseniz base classımızdaki KargoUcreti() metodu virtual olarak tanımlanmıştır, bu tanımlama bu metodun override edilebileceğini gösterir, yani buna izin verir, aksi takdirde override etmeniz mümkün değildir. Interface (Arayüz) Interface (arayüz ya da arabirim olarak Türkçeye çevirilmiş) bir sınıfın temelde hangi elemanlardan oluşmak zorunda olduğunu belirleyen şablondur, inheritance kavramında base bir sınıfımız vardı ve bu sınıfı miras alan derived class, base classtaki metodları, özellikleri vb. kullanabilirdi fakat bu zorunlu değildi. interface ise bunu mecburi kılan bir yapıdır, yani içerisindeki tüm elemanlar implement edilmek zorundadır, aksi takdirde class does not implement interface member InterfaceName’ şeklinde bir hata ile karşılaşırsınız. Bir class birden fazla sınıftan miras alamaz, fakat interface’lerin multi inhertitance desteği vardır, yani bir class birden fazla interface’den miras alabilir. interface içerisindeki metodlar default olarak abstract seviyesindedir ve içerisindeki tüm elemanlar public olarak tanımlıdır, interface’i de soyut bir sınıf olarak düşünebiliriz. Az önceki örnekten gidelim, online dükkanınızda sattığınız her ürün için bir kargo işlemi gerçekleştirmek zorundasınız. Bunun için ürün özelliklerini içeren sınıfınız olduğu gibi, bu ürün sınıfının kargo bilgileri de olmak zorunda olacağından bu urun sınıfı kargo bilgilerinin olduğu bir Interface’den implement olmak zorundadır. Bu konuştuklarımızın kod karşılığı aşağıdaki gibidir. interface IKargo { string KargoFirması { get; set; } string TeslimSuresi { get; set; } void KargoUcreti(); } public class Urun : IKargo { public string UrunAdi { get; set; } public double UrunFiyati { get; set; } public string KargoFirması { get; set; } public string TeslimSuresi { get; set; } public void KargoUcreti() { throw new NotImplementedException(); } }

Kod Yorumu: Interfaceler bir standart olarak i harfi ile başlar. interface içerisindeki tüm elemanların miras alınan sınıf tarafından implement edilmek zorunda olduğunu söylemiştik. IKargo interface’i içerisindeki tüm elemanların, onu miras alan Urun sınıfı içerisinde görüyoruz. Bir sınıf birden fazla interface’den miras alabileceğini söylemiştik, bunun için araya bir virgül eklemek yaterli. public class Urun : IKargo, IBaskaBirInterface, IBaskaBirInterfaceDaha, … Bir sınıf hem bir class, hem de birden fazla interface ile ilişkilendirilebilir. public class Urun : BaseClass, IBaskaBirInterface, IBaskaBirInterfaceDaha, … Son Olarak Bazı Notlar : Bu makaledeki örnekler c# dili baz alınarak hazırlandı, peki OOP sadece c# dili için geçerli bir kurallar kümesi değil, içinde bulunduğumuz zamanda pek çok dilin OOP desteği mevcut. (php, ruby vb.) sadece syntax farklılıkları var, bunları kısaca örnekleyecek olursak C# inheritance -> class Kitap : YayinEvi Php inheritance -> class Kitap extends YayinEvi Java inheritance -> class Kitap extends YayinEvi Ruby inheritance -> class GoodDog < Animal Görüldüğü gibi, c# dilinde “:” miras alınacak sınıf “:” işareti ile belirtilmişken, java ve php’de extends ile, ruby’de de “<” işareti ile belirtilmiştir. Çalışma mantığı aynıdır.

Bu makale, yazının başında da belirttiğim gibi benim notlarımdan oluşan, for beginners seviyesinde bir makale; oop bir makalede özetlenmeyecek kadar geniş bir konu, bu noktada okuduğum ve çok faydasını gördüğüm bir kitabı tavsiye etmek istiyorum, Beginning Object-Oriented Programming with C# - Jack Purdum. Kitabın kindle ve baskılı halini amazon’dan temin edebilirsiniz. Engin Tosun 2015 – http://engintosun.net Makalenin yayındaki link adresi: http://engintosun.net/Article/15/Object-oriented-programming-Temelleri (kaynak gösterilmek şartı ile kullanılabilir)