26 Mart 2016 Cumartesi

Nesne Yönelimli Programlama (Object Oriented Programming)

C#, Java gibi bir Nesne Yönelimli Programlama (OOP) dilidir. OOP 1960’lardan bugüne yazılım dünyasını etkisine almış bir metodolojidir. 1970 yılından bugüne kadar geliştirilen birçok dil OOP desteğine sahiptir.

OOP temel olarak, ortaya çıktığı güne kadar süregelen programlama mantığını kökten değiştirmiştir. OOP’den önce kullanılan yazılım metodolojisi, Prosedürel Programlama adı ile anılır. Bu metodoloji, belirli bir yönde ilerleyen kodlar ve iş yükünü hafifletmek için ortak işlerin yüklendiği fonksiyonların çağırılması esasına dayalıydı.
Bu metodoloji, yazılım dünyasının uzun süre işini görse de, bazı sıkıntıları da içinde barındırıyordu:

Geliştirilen uygulama parçalanamayan bir bütün halindeydi. Bu yüzden, uygulama üzerinde çalışan her geliştirici; uygulamanın hemen her yapısına hakim olmalıydı.
Bu durum nedeniyle, projelere yeni yazılımcıların katılması önemli bir adaptasyon süreci gerektiriyordu.
Uygulama tek bir bütün halinde olduğu için, ufak değişiklikler uygulamanın farklı noktalarında büyük sorunlara yol açabiliyordu.
Yıllarla birlikte müşteri ihtiyaçları ve donanım kabiliyetleri arttı. Bunun getirisi olarak da, geliştirilen uygulamaların kapsamları ve boyutları büyüdü. Bu aşamada, yukarıda bahsedilen problemler gittikçe artmaya başladı. Başlanan projelerin çoğu istenen sürelerde yetiştirilememeye ve geliştirme zorluklarından ötürü iptal olmaya başladı.
Uygulama maliyetleri giderek artmaya başladı.
Yazılım dünyasında bu çıkmazın aşılması OOP ile sağlanmıştır. OOP ile tüm yazılım anlayışı kökten değişmiştir. 1960’larda OOP fikrini ilk ortaya atan Alan Kay, önerdiği metodolojiyi şu şekilde ifade etmiştir:

Uygulama, nesneler ve onların ilişkileri çerçevesinde belirli bir iş yapmak için geliştirilebilmelidir.
Her nesnenin bir sınıfı olmalıdır ve sınıflar nesnelerin ortak davranışlarını ifade etmelidir.
Nesneler birbirleri ile iletişime geçebilmelidir.
Bunu basitçe, prosedürel programlamada bulunan soyut program geliştirme mantığını rafa kaldırıp, gerçek dünya modellemesi ile program geliştirme çabası olarak da düşünebiliriz. Gerçek dünya modellemesiyle anlatılmak istenen şudur: Bir fabrika örneğini ele alalım. Bu fabrikada işçiler, makineler gibi birçok nesne bulunur ve bu nesnelerin ilişkisi çerçevesinde fabrika çeşitli işler yapıp çıktılar üretebilir. OOP ile programlama mantığında da, bu örnektekine benzer şekilde program kurgulanır. Çeşitli nesneler geliştirilip birbirleriyle ilişkilendirilerek, belirli amaçlara hizmet eden uygulamalar geliştirilir.

Bu yapının önemli getirileri şunlardır: Yazacağınız sınıflar birbirinden bağımsız olarak geliştirilebilir. Bu sayede program böl, parçala, fethet mantığı çerçevesinde çok kolay bir şekilde parçalanır ve her parça ayrı ayrı ele alınabilir. OOP mantığında gerçek dünya algılayışı temel alındığı için bu, anlaşılması çok daha kolay bir yapıdır.

OOP, yapısı gereği kod tekrarlarının önüne geçer (doğru bir şekilde kullanılırsa) ve bu durum, özellikle ilk dönemlerde yazılımcıların hızlı bir şekilde OOP yapılarına geçmesinin temel nedenlerinden biri olmuştur.

Yukarıda değinilen getiriler, projelerin yönetilebilirliğini büyük miktarda artırdığı için daha büyük projeler çok daha az çaba ile yönetilebilir hale gelmiştir. Yine aynı getiriler sayesinde, projeler rahat bir şekilde büyütülebilmiştir. Alttaki grafikte noktalı çizgilerle gösterilen bölümler OOP’nin etkileridir. Yani, OOP ile yazılım kalitesi artmış, bunun yanısıra yazılım maliyetleri düşmüştür.






Sınıflar (Classes)

OOP temelde nesneler ve onların ilişkisi üzerine kurulu bir metodoloji olarak tanımlanmıştır. Ancak öncelikli olarak sınıf mantığının iyi anlaşılması gerekir. Nesneler sınıflardan türetilir ve yetenekleriyle yapabilecekleri sınıflarla belirtilir. Nesne-Sınıf ilişkisi için şöyle bir örnek verilebilir: İnsan bir sınıftır, ama her bir kişi, insan sınıfının bir nesnesidir. Örneğin, günlük kullanımda Ali bir insandır diyebiliyor olsak da, şu denklem yanlıştır: İnsan=Ali. Eğer sözlükte insan maddesinin yanında tanım olarak Ali’den bahsedilmiyorsa, bu eşitlik hiçbir zaman doğru olamaz. İnsan sınıfı bize Ali gibi her insanın yapabileceklerini, yeteneklerini ve özelliklerini belirtir. İnsan konuşabilir, el sallayabilir, koşabilir v.s. dediğimiz her seferinde, bu işlerin Ali için de geçerli olduğunu kabul etmiş oluruz. Her insanın bir göz rengi vardır dersek, Ali’nin de bir göz renginden bahsedebiliriz.


Programlarda da bu mantıktan hareket edilir. Bir düğme yapıp bunu kullanmak istiyorsanız, önce düğme sınıfını tanımlarsınız. Daha sonra da istediğiniz yerde düğme sınıfından örnekler alırsınız (instance almak). .NET Framework içinde tanımlanmış bir Button sınıfı bulunur. İsterseniz, tanım yapmak yerine, doğrudan kütüphane içindeki sınıftan da nesne türetebilirsiniz (instance alabilirsiniz). Alttaki örnekte, önce instance alınmış ve daha sonra, Text özelliğinin değeri belirtilmiştir. Sonra da, Click olayı olduğunda yapılacak işleri belirtmek için yenibuton.Click metodu oluşturulmuş ve işaret edilmiştir.

Button yenibuton = new Button();

yenibuton.Text = "Click me";
yenibuton.Click += yenibuton_Click;

private void yenibuton_Click(object sender, EventArgs e)
{
Console.WriteLine("Merhaba");
}


Birinci satırda, Button sınıfından bir örnek alınmıştır. İkinci satırda, oluşturulan yenibuton isimli Button nesnesinin Text özelliğine “Click me” yazısı atanır. Üçüncü satırda, yenibuton.Click += yazılır ve klavyeden Tab tuşuna basıldığında eşittirin karşısına yenibuton_Click yazılır. Bir kere daha Tab tuşuna basıldığında aşağıdaki butona Click edildiğinde çalışacak kod bloğunu oluşturacaktır.

Bu kod bloğunda parametre buton object, click işleminin argümanları EventArgs'e olarak taşınır.

Bu sayede düğmenin geliştirilmesine olan gereksinim ve bu geliştirmenin uygulama üzerindeki etkileri ortadan kaldırılmış, projenin daha rahat bir şekilde yönetilmesi sağlanmıştır.


Erişim Belirleyiciler(Access Modifiers)

Kendi sınıfınızı yazacaksanız, öncelikle C# dilinde nesnelerin birbirleriyle ilişki kurarken hangi erişim kontrolleri ile çalıştığını bilmeniz önemlidir. Erişim belirleyiciler (access modifiers), sınıflara nerelerden ve ne şekilde erişileceğini belirtir.

Public: Her yerden erişilebilir.
Private: Sadece tanımlandığı sınıf içerisinden erişilebilir.
Internal: Sadece bulunduğu projede erişilebilir.
Protected: Sadece tanımlandığı sınıfta ya da o sınıfı miras alan sınıflardan erişilebilir.
Protected Internal: Sadece tanımlandığı sınıfta ya da o sınıfı miras alan sınıflardan erişilebilir. Ayrıca tanımlamanın aynı proje içerisinde olma şartı yoktur. Protected'dan farkı budur.
Eğer erişim belirleyici belirtilmemişse, sınıflar internal’dır.

Sınıf İçindeki Yapılar

FIELDS:


  • Genellikle, nesnenin özellikleri (property) için değer saklama alanıdır. Varsayılan durumda private yapılardır.
  • Field, global alanda tanımlanmış bir değişken olarak da düşünülebilir. Yani class içerisindeki tüm metotlardan erişilebilecek bir değerdir.
public class Insan
{
private string _isim;
}


PROPERTIES:

Nesnenin özellikleridir.
Kendi içinde iki metot barındırabilir:
Set metodu: Bir property’ye yeni bir değer atamak için kullanılır. Set metodu olmayan property’ler read-only durumundadır.
Get metodu: Bir property’nin değerini okumak için kullanılır
Genelde bir field’da bulunan değeri değiştirmek / okumak için kullanılır.

private string _isim;
public string Isim
{
get
{
return _isim;
}
set
{
isim = value;
}
}



METHODS:

Nesnenin yapabildiği işlerdir.
Virtual, override, abstract metotlar oluşturulabilir. Bu sayede, miras alan sınıfın bu metot ile yapabileceği işler belirlenebilir.
Static olarak oluşturulabilir. Bu sayede, metot nesnenin değil sınıfın metotu haline gelir.

public void kos()
{
}

public static void aya_ayak_bas ()
{
}
Static metotlar o metoda başka sınıflardan instance alınmadan ulaşılmasına olanak sağlar.



CONSTRUCTOR

Constructor ismi, class’ın ismi ile aynı olmak zorundadır.
Nesne ortaya çıkartılırken yapılacak işlemleri barındırır. Sadece içine yazıldığı sınıftan bir nesne instance alınır bir defaya mahsus olarak çağrılır. Geriye değer döndürmez.
Farklı parametreleri alarak aşırı yüklenebilir (overload).

public Insan ()
{
This.Isim = “Doe”;
}

// Burada kullanılan This, işlem yapılan sınıfı ya da sayfayı temsil etmektedir.

public Insan(string adi)
{
This.Isim = adi;
}

EVENTS

Nesnelerin tepkileridir. Delegate’ler ile birlikte kullanılır. Düğmenin tıklanması bir event’tir. Daha önce düğmeyi tıklayınca bir tepki vereceği öngörülmüştür. Aşağıdaki örnekte, Kettle sınıfına SuKaynama Eventi eklenmiş ve su derecesi 100’ü geçerse bu event’in tetikleneceği belirtilmiştir. Artık Kettle nesnesi ile çalışırken su kaynayınca ne iş yapması gerektiğini söyleyebiliriz:

public delegate void SuKaynamaDelege();
public class Kettle
{
public event SuKaynamaDelege SuKaynamaEventi;
private byte _susicakligi;
public byte Susicakligi
{
get
{
return _susicakligi;
}
set
{
_susicakligi = value;
if (this._susicakligi >=100)
{
if (SuKaynamaEventi != null)
{
SuKaynamaEventi();
}
}
}
}
}
OOP sadece sınıf ve nesne ilişkisi değildir. Bunun dışında da kendine özgü bazı özellikleri bulunur. Bu özellikler OOP desteği veren dillerde tamamen veya kısmen dile eklemlenmiştir. Bu özellikler encapsulation (sarmalama/paketleme), inheritance (miras alma) ve polymorphism’dir (çok biçimlilik).

Const Kullanımı: Const program akışı içerisinde her hangi bir noktada değerinin sabit kalacağını ifade eder.

private const double pi = 3.14; // pi sabit sayısı gibi.

Enum

Bu tip, değişkenin alabileceği değerlerin belli (sabit) olduğu durumlarda programı daha okunabilir hale getirmek için kullanılır. Genel yazım biçimi şu şekildedir:

enum TipAdı{değer1,değer2,...,değerN} DeğişkenAdı;
TipAdı programcı tarafından verilen tip ismidir. DeğişkenAdı ise program içinde kullanılacak olan değişkenin adıdır. Eğer kullanılmazsa program içinde daha sonra enum ile birlikte kullanılır. Örneğin:

enum bolumler{programcilik, donanim, muhasebe, motor};
Tanımı ile derleyici programcilik için 0, donanim için 1, muhasebe için 2 ve motor için 3 değerini kabul ederek atamaları buna göre yapar. Değişken adı bildirilirse daha sonra enum kullanmaya gerek kalmaz.

enum renkler{kirmizi,mavi,sari} renk;
enum günler
{
pazartesi,sali,carsamba,persembe,cuma,cumartesi,Pazar
};
Şimdi istenirse tanımlanan bu tipler program içinde kullanılabilir.

enum bolumler bolum;
enum gunler gun;
bolum = muhasebe // bolum = 2 anlaminda
gun = cuma; // gun = 4 anlaminda
renk = kirmizi; // renk = 0 anlaminda


Sarmalama / Paketleme (Encapsulation)

Sarmalama / Paketleme (Encapsulation), bir nesnenin özelliklerinin dışarıdan kullanılmasını sınırlamak için kullanılır. Bir insanı örnek verelim. Vücudumuzun yaptığı işler sarmalanmıştır ve biz buna müdahale edemeyiz. Örneğin, biz sadece solumakla ilgileniriz; ciğeri şişirmek, kana oksijen karıştırmak, kirli kanı temizlemek gibi işlemler içeride yapılıyor olsa da, bizim için durum sadece solumaktan ibarettir. Diğer işlemler sarmalanmıştır. Aynı mantıkla, nesnelerimizin de bazı işleri kendi içinde yapması ve bu sayede daha kolay kullanılabilmesi sağlanır böylece veri güvenliği de sağlanmış olur.

Bu işlem yapılırken esasen access modifier’larla işlemlere ne şekillerde erişileceğini belirlemeniz yeterlidir.

İnsan örneğini kod halinde ifade edelim:

public class Insan
{
private void KirlikandakiKarbondioksitiAl()
{
//.......
}
private void OksijenikanaVer()
{
//.......
}
private void CigeriSisir()
{
//.......
}
private void CigeriDaralt()
{
//.......
}
public void NefesAl()
{
CigeriSisir();
OksijenikanaVer();
KirlikandakiKarbondioksitiAl();
CigeriDaralt();
}
}
Bu örnekte de görüldüğü gibi, Insan sınıfının sadece NefesAl metodu public olduğu için dışarıya açıktır. Bu nesneyi kullanan yapılar asla diğer metodlara erişemez, istese de değişikliğe sebebiyet veremez.

Aynı durumu daha gerçekçi bir örnekle inceleyelim:

public class TarihBilgisi
{
private DateTime tarih;
public string Gecerlitarih;
{
set { tarih = Convert.ToDateTime(value) }
}

private double GunOlaraktarihFarki(DateTime farkAlinacaktarih)
{
return (farkAlinacaktarih - tarih).TotalDays;
}

// Yukarıda yapılan işlemi incelediğimizde Gecerlitarih isimli property’e atılan değer dönüştürülerek DateTime tipindeki tarih isimli değişkene atılmaktadır. GunOlaraktarihFarki isimli metod parametre olarak almış olduğu farkAlinacaktarih isimli değişkenden tarih isimli değişkeni çıkarmaktadır. Kullanılan TotalDays özelliği çıkarılan iki tarih arasındaki farkı gün olarak hesaplamaktadır.

public string GunFarki(string Yenitarih)
{
return GunOlaraktarihFarki(Convert.ToDateTime(Yenitarih)).ToString() + " gün fark var";
}
}
Bu örnekte de görüldüğü gibi, tarih alanı (field) ve GunOlaraktarihFarki metodu private yapılarak dışarıdan gizlenmiştir. TarihBilgisi nesnesi ile çalışan bir uygulama bu alandan ve metottan haberdar olmayacak ve sadece bizim izin verdiğimiz yapılara erişerek işlerini görecektir. Kısıtlama kötü gibi görünse de, aslında bu sayede gereksiz alanlarla uğraşmasına gerek kalmamış ve değiştirmemesi gereken yerleri değiştirme şansı kalmamıştır.

Miras Alma / Kalıtım (Inheritance)

Inheritance (miras alma, kalıtım), bir nesnenin özelliklerinin farklı nesneler tarafından da kullanılabilmesine olanak sağlayan OOP özelliğidir. Yazılan bir sınıf bir başka sınıf tarafından miras alınabilir. Bu işlem yapıldığı zaman temel alınan sınıfın tüm özellikleri yeni sınıfa aktarılır.

İnsan – memeli ilişkisinde, insanın memeli sınıfını miras aldığı söylenebilir. Bu sayede insan sınıfını yazarken memelilerin özelliklerini tekrar yazmamıza gerek kalmaz. Elinizde bir taşıt sınıfı varsa; otomobil, kamyon, motosiklet gibi alt sınıfları üretmek çok daha az çaba gerektirir.

C# dilinde sadece tek bir sınıftan miras alabilirsiniz.

Örnek:


public class Canli
{

public int ayak; // Canlının kaç ayağı olduğunu tutan field
public int el; // Canlının kaç eli olduğunu tutan field
public string tur; // Ne tür bir canlı olduğunu belirleyeceğimiz field.
}


Şimdi iki adet daha sınıf oluşturarak Canli sınıfından türetelim:



class Insan : Canli
{
public void Konus()
{
Console.WriteLine(“Selam benim {0} adet elim ve {1} adet ayağım vardır.”,el,ayak);
Console.ReadLine();

}
}


Yukarıda farklı kullanılan bir yapı gözükmektedir. Console.WriteLine metodunun daha öncelerde farklı kullanımını görmüştük. Bu kullanımda ise indeks mantığı ile değişkenler atanmıştır. Yani “{0}” Console.WriteLine metoduna girilecek metnin ardından gelen parametrelerin ilkini yani index’i sıfır olanı ifade etmektedir. Bu da metodun kullanımında el isimli değişkene denk gelmektedir. Diğeri olan “{1}” ise bir sonraki indeksteki ayak isimli değişkene denk gelmektedir.



class Kopek : Canli
​{
public void Havla()
{
Console.WriteLine(“Hav”);
Console.ReadLine();
}
}


Bu sınıfların kullanım örneği aşağıda verilmiştir:



class CanliDemo
{
public static void Main()
{
Insan ins=new Insan();
Kopek kop=new Kopek();

ins.el=2;
ins.ayak=2;
kop.ayak=4;
ins.Konus();
kop.Havla();

}
}


Bu uygulamayı çalıştırdığınızda ekranda aşağıdaki çıktı görülür:

Selam benim 2 adet elim ve 2 adet ayağım vardır.

Hav

Görüldüğü gibi el ve ayak gibi özellikler canlıyı kalıtım alan sınıflarda bir daha yazılmamıştır. Insan ve Kopek sınıfları CanliDemo sınıfından türediği için bu özellikleri doğrudan edinmiştir.

Arayüz (Interface)

Arayüz (interface), OOP'nin en önemli yapılarındandır ve temelde sınıflara sunabileceğiniz, sınıfın hangi isimde ve hangi tipte parametreleri alan bir metoda sahip olacağını söylemenizi sağlayan yapıdır.

Bir sınıfa istediğiniz kadar arayüz uygulayabilir / giydirebilirsiniz (implement). Arayüz içine yazılan metotların işlemleri kesinlikle yazılmaz. Arayüzler hangi işin yapılması gerektiğini belirtir ama nasıl yapılacağına karışmaz. Bu metotların işlemleri, arayüzün uygulandığı sınıfta yazılır. Arayüzün access modifier'ı olsa da, içine yazılanların olmaz.

Arayüzler esas gücünü ve önemini çokbiçimlilikten (polymorphism) alır, çünkü çokbiçimlilik normal bir sınıfla yapılabildiği gibi, aynı arayüz giydirilmiş nesneler arasında da kullanılabilir. Arayüzler .NET Framework içinde de sıklıkla kullanılır.

Arayüzler sınıflara kural getirmek için kullanıldığı gibi, ortak kullanım alanları da oluştururlar. Örneğin; kayıt özelliği kazandırmak istediğimiz birtakım sınıfımız elimizde olsun (örn: Öğrenci, Öğretmen ve Ders). Bu sınıfların kayıt işlemleri için kullanacağı belli başlı metotlar olacaktır. (Örn: Kaydet, Sil, Düzenle ve VeriGetir gibi.) Bu kalıbı bir interface içerisine yerleştirdiğimizde ve kullanacak sınıflara giydirdiğimizde aslında bu sınıflar için kural koymuş oluruz.

Örnek olarak, foreach'in çalışabilmesi için nesne IEnumerable, listbox'ta bir nesneyi gösterebilmek için IList arayüzünü uygulamış nesneleri kullanır ve IList birçok dizi tabanlı yapının temelinde bulunur (listboxt.items, arraylist vb.).

Örnek:

interface KayitIslemleri
{
void Kaydet();
void Guncelle();
void Sil();
}

class Ogrenci:KayitIslemleri
{
public void Kaydet()
{
throw new NotImplementedException();
}
public void Guncelle()
{
throw new NotImplementedException();
}
public void Sil()
{
throw new NotImplementedException();
}
}

class Ogretmen : KayitIslemleri
{
public void Kaydet()
{
throw new NotImplementedException();
}
public void Guncelle()
{
throw new NotImplementedException();
}
public void Sil()
{
throw new NotImplementedException();
}}

Gördüğünüz gibi, üstteki örnekte Ogrenci ve Ogretmen sınıfı, Kayitislemleri arayüzünü uygular. Bu arayüzün uygulandığı sınıf, kaydet, güncelle, sil metoduna sahip olmak zorundadır. Arayüzler sayesinde yukarıda oluşturduğumuz yapıda ortak noktalar belirledik. Örneğini aldığımız sınıfın türünü bilmeden (Öğrenci veya Öğretmen) içerisinde Kaydet, Guncelle ve Sil metotlarının olduğunu bilmekteyiz. Bu sayede toplu işlemler yapmak istediğimiz zaman arayüzün bize getirdiği bu avantajdan faydalanabiliriz. Aşağıdaki örnekte toplu şekilde silme işlemi gerçekleştirilmektedir.

//örneği alınacak öğretmen ve öğrenci sınıfları bu dizinin içinde toplanacaktır.
KayitIslemleri[] ogrencilerveogretmenler = new KayitIslemleri[2];

Ogrenci ogrenci = new Ogrenci();
//bir üst satırda örneği alınmış Ogrenci sınıfı dizinin ilk sırasına yerleştiriliyor.
ogrencilerveogretmenler[0] = ogrenci;
Ogretmen ogretmen = new Ogretmen();
//bir üst satırda örneği alınmış Ogretmen sınıfı dizinin ikinci sırasına yerleştiriliyor.
ogrencilerveogretmenler[1] = ogretmen;

//dizi içerisinde gezilerek ilgili sıradaki nesnenin ne olduğu bilinmeden silme işlemi gerçekleştiriliyor.
for (int i = 0; i < ogrencilerveogretmenler.Length; i++)
{
ogrencilerveogretmenler[i].Sil();
}

Tanımları inceledikten sonra artık yavaş yavaş örneklerimize geçebiliriz.


Hiç yorum yok:

Yorum Gönder