Ana Sayfa | Eğitim Cd'leri | Eğitim Kitapları |  212 428 23 21-23 39


 

 

PERL'E DERSLERİ - 6

Sınıflar:
OOP türü yazdigimiz programlarda sadece nesneler vardir. Bu nesneler, mesaj alisverisi yaparak, birbirleriyle haberlesirler.
Nesneler OOP de sinif olarak ifade edilen sablonlardan yaratilir. Her sinif icinde, o siniftan üretilecek nesnelerin özelikleri, davranislari tespit edilir. Örnegin Araba isminde bir sinif yaratabiliriz. Bu siniftan üretilecek nesnelerin ortak yani, bir motor, 4 teker, direksiyon gibi parcalar olacaktir.
Siniflar, nesne üretme yaninda baska bir özellige daha sahiptir. Siniflar arasinda akraba ve is iliskileri kurmak mümkündür. Siniflar hakkinda daha genis bilgiyi, bu bölümün ilerleryen satirlarinda bulacaksiniz.

Nesneler:
OOP de nesneler siniflardan üretilir. Nesneler, siniflarin aksine canlidir ve kimlikleri vardir. Ayni siniftan üretilmis iki nesnenin sahip oldugu degiskenler degisik degerlere sahiptir. Örnegin araba sinifindan üretilen Mercedes ve BMW marka iki arabanin plaka numaralari degisiktir. Burada adi gecen plaka numarasi, sinif icinde yer alan bir özelliktir. Siniftan üretilen her nesne bu özelligi alir. Nesneler üretilirken, nesne özellikleri, sahip olduklari yapiya göre, degisik olacaktir. Araba örneginde oldugu gibi, Mercedes ve BMW nesneleri degisk plaka numaralarina sahiptir.
Yazilan OOP programlarda sadece nesneler ve aralarindaki interaksiyon söz konusudur

Metodlar:
Metodlar bir nesnenin dis dünyaya acilan kapilaridir. Bir nesnenin icinde ne oldugu, nesnenin nasil calistigi disaridan görünmez. Nesnenin sahip oldugu bir ic dünyasi vardir. Eger nesne ile interaksiyona girmek istiyorsak, disa acilan kapilarindan (metodlardan) birini kullanmamiz gerekir. Nesnelere, sinif icinde tanimlanmis ve her nesnenin sahip oldugu metodlar araciligiyla mesaj gönderebiliriz. Eger bir nesneden birsey yapmasini bekliyorsak, metodunu kullanarak, ona bir mesaj göndeririz. Nesne bu mesaji alarak, gerekli islemi yapar.
Metodlar nesnelerin dis dünyaya karsi davranislarini tanimlar. Metodlari, diger programlama dillerinde kullanilan fonksiyonlar olarak düsünebiliriz. Nesneler ile sadece bu metodlari kullanarak, interaksiyona girebiliriz.

Konstruktör:
Nesnelerin siniflardan yapildigini daha önce ögrendik. Nesneler üretilirken, sahip olduklari degiskenlerin degisik degerlerde olmasi gerekir. Araba örneginde oldugu gibi, Mercedes ve BMW marka iki nesnenin degisik plaka numaralarina sahip olmalari gerekir. Bu demek oluyor ki, nesneler üretiliken, onlara degisik kimlikler verecek bir mekanizmaya ihtiyacamiz var.
Bu görevi sinif icinde konstruktörler üstlenir. Nesneler konstruktörler araciligiyla üretilir. Konstruktör bir nesne üretmeden önce, nesne icin gerekli degisken degerleri belirlenir ve bu degerler parametre olarak konstruktöre gönderilir. Kontsruktör bu degerleri kullanarak, nesneyi üretir. Her sinif icinde, nesne üretmek icin bir kontruktör vardir.

Destruktör:
Her sinif icinde kontruktör yaninda birde destruktör bulunur. Destruktörün görevi hayati sona ermis bir nesneyi sistem üzerinden uzaklastirmaktir. Program bitiminde üretilen bütün nesneler destruktör araciligiyla yok edilir.

Miras:
OOP nin en büyük özelliklerinden birisi, siniflar arasindan miras birakma teknigiyle, yazilan kodun, defalarca baska bölümlerde kullanilabilmesidir.
Nesneler siniflardan üretilir. Tasit isminde bir sinifimiz oldugunu düsünün. Bu siniftan motorlu tasitlar ve motorsuz tasirlar isminde iki alt-sinif üretmemiz mümkündür. Bu üretilen iki alt sinif, tasit isimli sinifta olan tüm nesne metod ve degiskenleri miras olarak alirlar. Alt siniflardan bir gram kod eklemeden, nesneler üterebiliriz Alt siniflarda gerekli degisiklikleri yaparak, bu sinifin nesnelerini özellestirebiliriz. Örnegin motorlu tasit sinifina, beygirgücü isminde bir degisken ekleyebilriz. Bu alt siniftan üretilecek bir nesne, tasit isimli sinifta olan tüm metod ve degiskenleri miras olarak aldigi gibi, ayrica beygirgücü isminde yeni bir degiskene daha sahip olacaktir.
Gördügümüz gibi, miras birakma teknigi ile, bir kere yazilan kod, defalarca kullanilabilir. Miras (inheritance) teknigi bunu mümkün kiliyor. Miras birakmanin ve almamini nasil kullanildigini, asagida yer alan örnegimizde görecegiz.
C++ ve Java dillerinde oldugu gibi, Perl dilinde OOP tarzi programlar yazmak mümkündür. Perl dilinde OOP tarzi programlarin nasil yazildigini incelemeden, C++ dilinde bir programi inceleyelim. OOP terimlerini bu örnek üzerinde göstermek istiyorum. Daha sonra bu programin Perl dilinde nasil yazilacagini görecegiz.
Yazacagimiz örnekte Araba isimli bir sinif yer alacak. Miras birakma teknigini kullanarak, Mercedes ve BMW isimlerinde iki alt-sinif üretecegiz. Bu altsiniflara, Araba anasinifinda yer alan tüm metod ve degiskenlerin miras kalmasi yaninda, altsiniflar, kendileri icin gerekli degiskenleri de tanimlayacaklar.
Bu C++ örnegini MS Visual C++ 6.0 programi ile derledim. Programi derlemek icin herhangi bir C++ derleyicini kullanabilirsiniz.

Program asagida yer alan dosyalar icinde yer alacak:
" Araba sinifi deklarasyonu, Araba.h,
" Araba sinifi definisyonu, Araba.cpp
" Mercedes altsinifi deklarasyonu, Mercedes.h,
" Mercedes altsinifi definisyonu, Mercedes.cpp,
" BMW altsinifi deklarasyonu, BMW.h,
" BMW altsinifi definisyonu, BMW.cpp,
" Ana program main.cpp
// Araba.h - Araba sinif deklarasyonu
class Araba
{
public: Araba();
void print();
Araba(char*, int);
~Araba();
protected:
int beygirgucu;
char* plakano;
};


Class direktifi ile bir sinif tanimlanir. Public bölümünde, sinif icinde yer alacak ve sonra üretilen nesneler tarafindan kullanilcak metodlar yer alir. Nesneleri üretmek icin Araba(char*, int) kontruktörü kullanilir. Konsruktöre bir string ve integer parametre olarak gönderilir. Bunun ne anlama geldigini daha sonra görecegiz.
Protected bölümü icinde, nesnelerin sahip olacaklari degiskenler yer alir. Protected ve Private sayesinde, bu degiskenler, disaridan gelebilecek saldirilara karsi korunur. Bu degiskenler sadece sinif metodlari kullanilarak degistirilebilir. OOP de nesnelerin ic hayatlarinin korunmasi önemli bir prensiptir. Hicbir sekilde, nesnenin sahip oldugu degerler disardan degistirilememelidir. Bu islem, metodlar arciligiyla nesneye mesaj gönderilerek yapilabilir. Araba sinifinin beygirgucu ve plakano isminde iki degiskeni vardir. Buna göre, bu siniftan üretilen her nesne yukarda yer alan metod ve degiskenlere sahip olacaktir. Konstruktör kullanilarak, her nesneye baska bir baslangi degeri verilir. Bu sadeye üretilen her nesne baska bilgileri tasir.

// Araba.cpp - Araba sinif definisyonu
#include <iostream.h>
#include "Araba.h"
// Konstruktör
Araba::Araba(char* plaka, int ps)
{
plakano=plaka;
beygirgucu=ps;
}
//Destruktör
Araba::~Araba() { }
//Print metodu
void Araba::print()
{
cout<<"Plaka:"<< plakano<<"Beygirgucu:"<<beygirgucu;
// Standart kontruktör
Araba::Araba()
{
beygirgucu=0;
plakano=0;
}


Araba.cpp dosyasinda sinifin metodlari yazilir. Araba::Araba(char* plaka, int ps) seklinde kontruktörü tanimliyoruz. Bu konstruktörü kullanarak, üretecegimiz nesnelere baslangic degerleri verecegiz.

// Mercedes.h - Mercedes sinif deklarasyonu
#include "Araba.h"
class Mercedes : public Araba
{
public:
void print();
Mercedes(char*, char*, int);
~Mercedes();
private:
char* model;
};


Araba sinifindan, class Mercedes : public Araba tanimlamasini kullanarak, Mercedes isminde bir altsinif olusturuyoruz. Bu andan itibare, Mercedes sinifi, Araba sinifinin tüm özellikleri alir.
Araba ve Mercedes siniflari arasinda baglanti kurabilimek icin #include "Araba.h" direktifiyle, Araba.h dosyayini Mercedes.h dosyayina ekliyoruz.
Mercedes(char*, char*, int); konstruktörü ile, Mercedes Marka arabalari üretecegiz. Araba konstruktörünün aksine bu kontruktör üc parametre aliyor. Mercedes marka bir araba ürettigimizde, plakano ve beygirgücü degiskenleri yaninda, Mercedes konstruktörüne model degiskeninide parametre olarak, gönderecegiz. Gördügünüz gibi, Mercedes, Araba'nin alt sifini oldugundan, Mercedes sinifi icinde plakano ve beygirgucu degiskenlerini kolayca kullanabiliyoruz.

// Mercedes.cpp - Mercedes sinif definisyonu
// Konstruktör
Mercedes::Mercedes(char* mod, char* plaka, int ps) :Araba(plaka,ps)
{
model=mod;
}
// Destruktör
Mercedes::~Mercedes() { }
// Araba sinifindan aldigimiz ve redefine ettiginiz Print metodu
void Mercedes::print()
{
cout<<"Model:"<<model;
Araba::print();
}


Mercedes.cpp dosyasi icinde dikaktinizi ceken satir Mercedes::Mercedes(char* mod, char* plaka, int ps) :Araba(plaka,ps) olmustur. Mercedes marka bir araba üretebilmek icin model, plakano ve beygirgucu degiskenlerini parametre olarak Mercedes Kontruktörüne gönderiyoruz. Arabamiz bu degerlerle üretilecektir.
Peki :Araba(plaka,ps) ne anlama geliyor?
Altsiniflardan üretilen her nesne icinde, ana siniftan üretilmis bir nesne yer alir. Altsinif icinde kod eklemesi yapmadan, ana sinifta yer alan metod ve degiskenleri kullanmamiz, nesne icinde yer alan, anasinifi nesnesi üzerinden olur. Nesneler üretilirken, konstruktörler aracigiyla istenen baslangic degerleri tespit edilir. Her sinif icinde bir standart konstruktör vardir. Alt siniftan bir nesne ürettigimizde, bu nesne icinde yer alacak anasinif nesnesininde bir baslangic degerine sahip olmasi gerekir. Bunu : :Araba(plaka,ps) seklinde bir tanimlamayla yapabiliriz. Mercedes nesnesi icin model, plakano ve beygirgucu parametrelerini alarak, baslangic degerlerini belirliyoruz. Model degiskeni Mercedes icinde yer aldigi icin, esleme bu sinif icinde yapiliyor. Diger iki parametre, ana sinif icinde bulunan degiskenlere eslenmesi gerektigi icin, Araba(plaka, ps) seklinde anasinif konstruktörüne gönderilir. Ana sinif bu parametreleri alarak, degiskenlere gerekli baslangic degelerini verecektir. Böylece Mercedes nesnesi gecerli baslangic degerlerine sahip olacaktir.
Nesneler, anasiniftan miras kalan tüm metod ve degiskenleri kullanabilirler. Isteyen altsiniflar, anasiniftan aldiklari metodlar üzerinde degisklik yapabilirler. Yukardaki örnekte, Mercedes sinifi, anasinif icinde tanimlanmis print() metodunu alarak, model degiskeninin degerini ekrana basmak icin degistirmistir. Araba::print(); komutuyla, anasinif icinde tanimlanan print() fonksiyonu calistirilir. Bu fonsiyon plakano ve beygirgucu degiskenlerinin degerini ekrana basar. Mercedes sinifinda, miras kalan print() fonksiyonu genisletilerek, plakano ve beygirgucu degiskenleri yaninda model degiskeninin degeride ekrana basilmis olur.

PERL'E DERSLERİ - 7

// BMW.h - BMW sinif deklarasyonu
#include "Araba.h"
class BMW : public Araba
{
public: BMW(char*, int);
~BMW();
};
// BMW.cpp - BMW sinif definisyonu
// Konstruktör
BMW::BMW(char* x, int y):Araba(x,y)
{ }
// Destruktör BMW::~BMW() { }

BWM sinifi icin gerekli tanimlari yaptikran sonra, ana programimizi yazabiliriz.

//main.cpp
#include <iostream.h>
#include "Araba.h"
#include "Mercedes.h"
#include "BMW.h"
void main(void)
{
Mercedes m1("E-200","34-MSB-24",90);
BMW b1("34-FO-060",150);
m1.print();
b1.print();
}


#include direktifi ile, ana- ve altsinif kodlari ana programa eklenir. Mercedes m1("E-200","35-AH-119",90); komutu ile m1 isminde Mercedes sinifindan bir nesne ürettik. Parantez icinde, Mercedes sinifinin kontruktörüne gönderilmek üzere parametreler yer aliyor. Nesnenin sahip oldugu degiskenleri ve degerlerini ekrana basmak icin print() fonksiyonunu kullanacagiz. print() fonksiyonu, Mercedes altsinifina Araba sinifindan miras kalmisti. m1.print(); komutu ile m1 isimli nesneye mesaj göndererek, print() fonksiyonunu calistirmasini söylüyoruz.
Ekran cikisi:

Model: E-200 Plaka: 34-MSB-24 Beygirgucu: 90

Print() metodunu Mercedes altsinifinda degistirdigimiz icin, model degiskeninin degeri de digerlerinin yanida ekrana basilir. Eger Mercedes sinifinda print() isminde bir fonksiyon tanimlamamis olsaydik, nesne, anasinif icinde yer alan print() fonsiyonunu calistiracakti ve ekran cikisi söyle olacakti:

Plaka: 34-MSB-24 Beygirgucu: 90

Ana sinifta yer alan print() fonsiyonunun, Mercedes sinifinda yeralan model degiskeninden haberi olmadigi icin, sadece plakano ve beygirgucu degiskenlerini ekrana basar. Gercektende ana siniflar, altsiniflarinda hangi metodlarin ve degiskenlerin yer aldigini bilmezler. Bunun aksine, alt siniflar, anasinifta yer alan tüm metod ve degiskenleri tanirlar.
OOP yi ve kullanilan terimleri tanidiktan sonra, bu örnegi Perl dilinde yazalim.

#!/usr/bin/Perl
use strict;
print "Content-type: text/html\n\n";
######### Class Araba######
package Araba;
sub araba
{
my $class=shift;
my $this={};
$this->{'plakano'}=shift;
$this->{'beygirgucu'}=shift;
bless($this, $class);
}


Perl'de siniflar paket (package) olarak tanimlanir. C++ örneginde oldugu gibi, sinif isminde bir konstruktör tanimliyoruz. shift, bir subrutine gönderilen parametrelere sirayla ulasabiliriz. Yeni bir nesne üremek icin araba() kontruktörü calistirildiginda, shift üzerinden sirasiyla ulasabilecegimiz bilgiler: sinifismi, plakano ve beygirgücüdür.
Önce shift icinde bulunan ilk bilgi (sinif ismi) $class degiskenine esitlenir. $class degiskeni yardimiyla, üretilecek nesnenin hangi siniftan oldugu tespit edilir. $this, C++ dilinde de kullanilan bir referanstir. Perl de nesnelere referanslar üzerinden ulasilir. Nesnenin sahip oldugu degisken degelerini yerlestirmek icin hash tipi bir liste kullanilir. bless fonksiyonu ile nesne canlanir.

sub print
{
my $this=shift;
print "$this->{'plakano'}\n";
print "$this->{'beygirgucu'}\n";
}


print() subrutini nesne degiskenlerinin degerini ekrana basar. Herhangi bir nesne tarafindan bu metod cagrilidiginda, metoda, shift üzerinden nesne kimligi gönderilir. Bu kimligi $this referansina esitleyerek, subrutin icinde nesne icin gerekli islemleri yapabiliriz.
print "$this->{'plakano'}\n"; ile nesnenin sahip oldugu $plakano isimli degiskenin degeri ekrana basilir. Daha öncede belirttigim gibi, bir nesnenin tüm degisken ve degerleri hash tipi bir listede saklanir. Bu liste araba() konstruktörü tarafindan yaratilir. $this üzerinden nesnenin sahip oldugu tüm degiskenlere ulasabiliriz. ;

sub DESTROY
{
print "Nesnenin hayati son buldu.";
}


Program bitimiyle tüm nesnelerin hayatina son verilir. Nesnenin yasamina son verilmeden önce, DESTROY metodu cagrilir. DESTROY sinif icinde destruktör vazifesi görür.

###### Class Mercedes #######
package Mercedes;
use vars qw(@ISA);
@ISA=qw(Araba);

Perl dilinde altsiniflara miras birakma islemi @ISA listesi üzerinden gerceklesir. @ISA icinde tüm üstsiniflar tanimlanir. Bu konuyu detayli olarak ilerleyen satirlarda inceleyecegiz.

# Kontruktör
sub mercedes
{
my $class=shift;
my $model=shift;
my $plaka=shift;
my $ps=shift;
my $this=Araba->araba($plaka,$ps);
$this->{'model'}=$model;
bless($this, $class);
}
sub print
{
my $this=shift;
print "$this->{'model'}\n";
print "$this->{'plakano'}\n";
print "$this->{'beygirgucu'}\n";
}


Mercedes sinifini Araba sinifinin altsinifi olarak tanimliyoruz. Mercedes @ISA listesin üzerinden bütün Araba metod ve degiskenlerine ulasabilir. Simdi ana programi yazalim:

package main;
my $b1=Mercedes->mercedes("E-200","34-MSB-24",95);
$b1->print();


Ana program yazmak icin main isminde bir paket tanimlamamiz gerekiyor. Daha sonra Mercedes sinifinda yer alan mercedes konstruktörünü kullanarak, $b1 isminde bir nesne olusturuyoruz. $b1 bir referanstir. Bu referansi kullanarak, print() metodunu cagiriyoruz.
Program icinde dikkatinizi cekmis olabilecek birkac noktaya deginmek isiyorum:

* C++ aksine, Perl dilinde konstruktörler derleyici tarafinda otomatik olarak calistirilmaz. C++ dilinde, sinif isminde bir konstruktörün tanimlanmis olmasi gerekir. Perl de böyle bir zorunluluk yok. Konstruktör herhangi bir isimde olabilir. Nesneleri olusturmak icin konstruktörü calistirmaniz gerekir.
* Nesneleri yok etmek icin destruktörü calistirmaniz gerekmez. Bu islemi paketin sahip oldugu destruktör otomatik olarak yapar.
* Bir sinifin metodlarina degisik sekilde parametre girisi yapilir. Örnegin konstruktörler parametre olarak önce sinif ismini alirlar. Normal sinif metodlarina önce nesneye ait $this referansi gönderilir.
* Siniflar arasi miras birakma ve alma @ISA listesi üzerinden gerceklesir. Bu liste icinde tüm ana siniflar yer alir.
* C++ dilinde yer alan public, private, protected koruma mekanizmalari Perl dilinde yoktur.
* Sinif tanimlamak icin kullanilan class gibi özel bir kelime yoktur.
* Metodlar sinif icinde tanimlanir. Sinif icinde kullanilan degiskenlere, hash tipi bir liste üzerinden ulasilabilir. Üretilen her nesnenin, degiskenlerini ve degerlerini sakladigi hash tipi bir listesi vardir.
* Nesneler Perl'de hangi sinifi ait oldugunu bilen referanslardir. Nesne hangi sinifa ait oldugunu bless() fonksiyonu araciligiyla ögrenir. Her konstruktörün en son satirinda bless() fonksiyonu yer alir.

 

Murat KUZU