3 Nisan 2011 Pazar

Dinozor gözü, Polymorphism ve Cast artı Interface (Polimorfizm, Çok biçimlilik, Arayüz)

dinosaur_moviePolymorphism, bir nesneyi size uygun olan kalıba döküp (cast) onun üzerinde işlem yapmaktır.
Vikipedide böyle yazıyor: “Çok biçimlilik bir A türünün bir başka tür olan B gibi davranabilme ve bu tür gibi kullanılabilme özelliğidir.

Aşağıdakı hikaye kısmının yerine geçebilecek bir çalışma:
Öyle bir metot yaz ki, ona parametre olarak verilen nesnenin tipinden asılı olmayarak, o nesnenin SuperBirFonksiyon() metodunu çağırsın. Nesne o metodu mutlaka içerecek. O metodun istenildiği gibi çalışmasını sağlamak için metodun dışında da her hangi kod yazabilirsin (, hatta yazmalısın).

“Ha, bu niçin gerek?” Hikaye kısmı.
Diyelim ki, hayali bir insan göz doktoru var.
Göz doktoru, tabii ki göz inceler. Şimdi dünyada tüm varlıkların göz yapısı yaklaşık aynı olduğunu varsayalım. (Belki bu dediğim gerçeğe yakındır.)
Doktor hep insan gözü ile ilgilenmiş, ‘insan gözü doktoru’ gibi kendini tanımlamış.
Bir gün ise ona dinozor getirmişler. Şaşırıp kalmış. Hani ben insan gözü doktoruyum da, dinozor gözünü nasıl incelerim ki? Böylece kalmış.
Şimdi hatırlayalım ki, dünyada tüm varlıkların göz yapısını aynı kabul etmiştik. Oysa, dünyada çok tür canlı var. Her birisi için ayrı doktor mu hazırlayacağız şimdi?
Akıl diyor ki, öyle olmaz.
O zaman, doktorları hep ‘insan gözü doktoru’ değil, ‘gözlü-canlı gözü doktoru’ gibi eğitirsek doktorlar yukarıdakı hayali doktor gibi şaşırıp kalmaz.
Bununlar yanaşı, doktorun canlının türüne göre göz aramaması için böyle bir şey yapalım: bir delikli kutu alıp, doktora canlıları o kutuda sunarız, canlının gözünü kutunun deliğine dayayıp. Böylelikle, hem insana, hem dinozora ‘gozlü-canlı’ gibi muammele ede biliyoruz.
Şimdi düşün, doktora ne kadar kolay ve universal bir yol sunuldu – hep aynı şekildeki kutunun aynı yerindeki deliğinden, farklı-farklı türlü canlıların gözünü inceleyebiliyor. Soyutlaştırmanın zaferi!
Mühteşem ya!

Polymorphism (çok biçimlilik) kavramı, ve onü mümkün eden casting (çevirim) işlemi de işte böyle bir şeydir.

”Programlamada nasıl karşımıza çıkar?”
Burada yazdığım kodlar Java dilinde, lakin genel mantık tüm bilgisayar bilimi dalına ait.
Diyelim ki, iki figürun alanların toplayan metot yazıyorsun
(ilkel devir, ‘tekrar biliyin anasıdır’ modu):

   1: class Kare{
   2:     private int alan;
   3:     public int getAlan(){ return this.alan; }
   4:     // ... burada Kare`ye has metot ve alanların kodu
   5: }
   6:  
   7: class Daire{
   8:     private int alan;
   9:     public int getAlan(){ return this.alan; }
  10:     // ... burada Daireye`ye has metot ve alanların kodu
  11: }
  12:  
  13: class AlanIsleri{
  14:     public static int AlanlariTopla(Kare figur1, Kare figur2){
  15:         return figur1.getAlan() + figur2.getAlan();
  16:     }
  17:     public static int AlanlariTopla(Daire figur1, Daire figur2){
  18:         return figur1.getAlan() + figur2.getAlan();
  19:     }
  20:     public static int AlanlariTopla(Daire figur1, Kare figur2){
  21:         return figur1.getAlan() + figur2.getAlan();
  22:     }
  23: }


Şimdi düşün, daha çok farklı figür türü ola bilir (ve var da) – üçgen, beşgen, … .
Onların hepisi için ayrı bir metod yazmak yerine, doktora dinozor getirmiş gibi programcı gibi düşünelim.
Metodun parametrelerine bakalım önce. Her defa farklı tipde parametre alıyoruz.
Sonra, içeriğine bakalım. Görüyoruz ki, kodumuz hep aynı: “figur1.getAlan() + figur2.getAlan()”.
Bu ne demek oluyor? Aynı kodu yazmak için, belli aynı isimli metotları çağırmak için farklı-farklı metodlar tanımlıyoruz, amma aynı içerikli! Yükarıdakı ‘İNSAN gözü doktoru’ olayı ile benzeşme hiss ediyor musun? ;)
Keşke Kare`leri, Daire`leri de böyle güzel, tanıdığımız delikleri olan bir kutuya salabilsek, sadece gereken o metoduna ulaştıracak kutuya…

“Yaşasın Kommunizm Polymorphism Çok Biçimlilik!”
Ve işte burda casting (çevirim, kalıba dökme) yardımımıza geliyor.
En ilkel şekilde, tüm figür tiplerini ‘Figur’ adlı ortak üstklastan genişleteceğim (extend). Sonra da AlanlariTopla metotların hepsini sileceğim, birinden başka. O birinin de parametrelerinin tipini Figur türünde alacağım
(kitle imha silahları devri, ‘bir mermi ile tüm nesli’ modu):

   1: class Figur{
   2:     private int alan;
   3:     public int getAlan(){ return this.alan; }
   4: }
   5: class Kare extends Figur{
   6:     // ... burada Kare`ye has metot ve alanların kodu
   7: }
   8:  
   9: class Daire extends Figur{
  10:     // ... burada Daire`ye has metot ve alanların kodu
  11: }
  12:  
  13: class AlanIsleri{
  14:     public static int AlanlariTopla(Figur figur1, Figur figur2){
  15:         return figur1.getAlan() + figur2.getAlan();
  16:     }
  17: }

İşte! Figur`un tüm altklasları için geçerli tek bir metot.

Üzüntü notu: Bu kod, sadece ortak ataları olan klaslar için geçerli. Yani, (engel:) getAlan metoduna sahip, lakin, Figur`un altklası olmayan hiç bir klas için geçerli değil. Yani, dinozoru ‘gözlü-canlı’ kalıbına koysa bile, kalıba sokma şeklini gözünün (getAlan`ın) olmasına göre değil, insan ile ortak özelliklerine (atasına, Figur`a) göre belirliyor (O_O). :(.

Hatırlatma notu:
Yalnız bir klassı genişletebiliyoruz, dolayısıyla bu engeli kaldırmak için iki farklı ata koyamayız, extend A, B diyemeyiz.

Tarihi not: Bunun (hatırlattığımın) nedeni, farklı klaslardan kalıtım alma – farklı klasları genişletme – prorgamlamada önceden görülemeyen hatalara izin veriyordu. C++`da birden fazla klası genişletmek mümkün. Java da ordakı hataların tecrübesinden bu imkanı aradan kaldırmaya karar vermiş.

“Tıpta gelişim!”
doktorların kalıblama asistanların eğitiminden sonra…
Fikrin ve prorgamlama dillerinin gelişimi bize arayüzleri (interfeys, interface) sundu.

İnterface: bir artısı var, tanımlamada çoklu implements A, B diye yaza biliyoruz. Bu artıyı dengelemek için bir eksisi var, interface`te metodun içeriğini yazmak gayrı-mümkün.

Arayüzde kullanılacak fonksiyonları tanımlanır. Arayüzü uygulayan (implement) klas arayüzün tüm taleplerini hayata geçirmek – metodları yazmak zorunda. Arayüz, bir nev anlaşma gibidir.

“Arayüz yaşasın mı?”
(son devir, ‘kalıba sok ve bak’):

   1: interface AlaniOlan{
   2:     int getAlan();
   3: }
   4: class Figur implements AlaniOlan{
   5:     private int alan;
   6:     public int getAlan(){ return this.alan; }
   7: }
   8: class Kare extends Figur{
   9:     // ... burada Kare`ye has metot ve alanların kodu
  10: }
  11:  
  12: class Daire extends Figur{
  13:     // ... burada Daire`ye has metot ve alanların kodu
  14: }
  15:  
  16: class HardDisk implements AlaniOlan{
  17:     public int getAlan(){ return Integer.MAX_VALUE; }
  18: }
  19:  
  20: class AlanIsleri{
  21:     public static int AlanlariTopla(AlaniOlan figur1, AlaniOlan figur2){
  22:         return figur1.getAlan() + figur2.getAlan();
  23:     }
  24: }
  25:  
  26: public class Main {
  27:     public static void main(String[] args) {
  28:         Kare k1 = new Kare();
  29:         HardDisk d1 = new HardDisk();
  30:         int alantoplami = AlanIsleri.AlanlariTopla(d1, k1);
  31:     }
  32: }

Buyurun. Soyutlaştırmanın zaferi arayüz uygulamasında.

“Cast yaptığımın mı verisi gelir, üst sınıfın mı?”
Ya…
Dinozoru kutuya koyduğuna, içindeki yine de dinozor, değil mi?
Sadece, kutunun (kalıbın) sağladığı deliklerden baka ve el gezdirebiliyorsun, o kadar.

<Kalıp Tip burada> a = new <Hafızada Ayrılacak Tip burada>
A a = new B(); // dersen
// 'a.metot()', 'a.alan' diyebilmen için 'metot()' ve 'alan' A klasında tanımlı olmalı.
a.AKlassindaTanimliFonk();
// Lakin, fonkun içeriği B`den çalışır.
a.klassIsmimiYazdir(); // ekrana B çıkar.
Bu arada A ile B arasında akrabalık alakası olduğunu unutmayın.

Yani, sanki A`ya bakıyorsunuz, amma aslında içindeki B.
İşaretciler (metotlar ve değişkenler) KalıpTipi`ın tanımladığı, işlemler Hafızada Ayrılmış Tip`in.
Delikler gözlünün, göz dinozorun. :)

 

 

 

dinosaur245x289






Yürek sözü olarak,
sana kalıplarsız düşünmeği arz ediyorum.

Hiç yorum yok:

Yorum Gönder