Nesne Yönelimli Programlamaya Giriş: Kavramlar ve İlkeler
12 dakikalık okuma
16 May 2023

Nesne Yönelimli Programlamaya Giriş: Kavramlar ve İlkeler

Nesne yönelimli programlama (OOP), yazılım geliştirmeye yapılandırılmış ve modüler bir yaklaşım sağlayan popüler bir programlama paradigmasıdır. Geliştiricilerin karmaşık sistemleri daha küçük, yönetilebilir bileşenlere ayırarak oluşturmasına olanak tanır. Bu makale, nesne yönelimli programlamanın temel kavramlarına ve ilkelerine bir giriş niteliğindedir ve bunların PHP programlama dili kullanılarak nasıl uygulandığını araştırır.

Nesne Yönelimli Programlama nedir?

Nesne yönelimli programlama, sınıf örnekleri olan nesneler oluşturmaya odaklanan bir programlama paradigmasıdır. Gerçek dünyadaki varlıkları, her birinin kendi özellikleri (nitelikleri) ve davranışları (yöntemleri) olan nesneler olarak temsil etme fikri etrafında döner. Nesneler, belirli eylemleri veya davranışları tetikleyen mesajlar göndererek birbirleriyle etkileşime girer.

OOP dört ana ilke üzerine inşa edilmiştir:

  1. Kapsülleme: Kapsülleme, bir nesnenin dahili uygulama ayrıntılarını gizleme ve iyi tanımlanmış arabirimler yoluyla yalnızca gerekli bilgileri açığa çıkarma işlemidir. Veri güvenliği ve kod bakımının sağlanmasına yardımcı olur.

  2. Kalıtım: Kalıtım, nesnelerin özellikleri ve davranışları diğer nesnelerden devralmasına olanak tanır. Kodun yeniden kullanımını teşvik eder ve sınıflar arasında hiyerarşik ilişkilerin oluşturulmasına izin verir. PHP'de sınıflar, 'extends' anahtar sözcüğünü kullanarak tek bir üst sınıftan miras alabilir.

  3. Polimorfizm: Polimorfizm, farklı sınıflardaki nesnelerin ortak bir üst sınıfın nesneleri gibi ele alınmasına olanak tanır. Birden çok formu veya türü temsil etmek için tek bir arabirimin kullanılmasını sağlar. Polimorfizm, yöntem geçersiz kılma veya yöntem aşırı yükleme yoluyla elde edilebilir.

  4. Soyutlama: Soyutlama, karmaşık sistemleri daha küçük, daha yönetilebilir bileşenlere ayırarak basitleştirmeyi içerir. Gereksiz ayrıntıları gizlerken bir nesnenin temel özelliklerini ve davranışını yakalamaya odaklanır. Soyut sınıflar ve arayüzler, PHP'de soyut türleri tanımlamak için kullanılır.

Nesneler ve Sınıflar

Nesne yönelimli programlamada, nesneler sınıf örnekleridir. Bir sınıf, nesneler oluşturmak için bir plan veya şablon görevi görür. Bir nesnenin sahip olacağı nitelikleri (veri üyeleri) ve davranışları (yöntemler) tanımlar.

PHP'de bir Araba sınıfı örneği ele alalım:

class Araba {
    public $renk;
    public $marka;
    
    public function motoruCalistir() {
        echo "Motor Çalıştı!";
    }
    
    public function sur() {
        echo "Araba sürülüyor...";
    }
}

Yukarıdaki örnekte, "Araba" sınıfının iki özelliği vardır: "$color" ve "$brand" ve iki yöntemi: "startEngine()" ve "drive()". Bu nitelikler ve yöntemler, bir araba nesnesinin özelliklerini ve davranışlarını tanımlar.

Car sınıfından bir nesne oluşturmak ve özelliklerine ve yöntemlerine erişmek için aşağıdakileri yapabiliriz:

$arabam = new Araba();
$arabam->renk = "Mavi";
$arabam->marka = "Toyota";

$arabam->motoruCalistir(); // Çıktı: Motor Çalıştı!
$arabam->sur(); // Çıktı: Araba sürülüyor...

Kapsülleme

Kapsülleme, nesne yönelimli programlamanın temel ilkelerinden biridir. Verileri (öznitelikleri) ve bu veriler üzerinde çalışan yöntemleri nesne adı verilen tek bir birimde bir araya getirmeyi içerir. Nesne, iç ayrıntıları kapsar ve diğer nesnelerin onunla etkileşime girebileceği bir arayüz sağlar.

Dahili ayrıntıları kapsülleyerek, nesnenin verilerine harici varlıklardan doğrudan erişimi önleyen veri gizlemeyi başarıyoruz. Bunun yerine, verilere erişim alıcı ve ayarlayıcı yöntemlerle kontrol edilerek veri bütünlüğü sağlanır ve bir düzeyde soyutlama sağlanır.

Kapsülleme ile Araba sınıfımızı geliştirelim:

class Araba {
    private $renk;
    private $marka;
    
    public function getRenk() {
        return $this->renk;
    }
    
    public function setRenk($renk) {
        $this->renk = $renk;
    }
    
    public function getMarka() {
        return $this->marka;
    }
    
    public function setMarka($marka) {
        $this->brandmarka= $marka;
    }
    
    public function motoruCalistir() {
        echo "Motor Çalıştı!";
    }
    
    public function sur() {
        echo "Araba sürülüyor...";
    }
}

$arabam = new Car();
$arabam->setRenk("Mavi");
$arabam->setMarka("Toyota");

echo $arabam->getRenk(); // Çıktı: Mavi
echo $arabam->getMarka(); // Çıktı: Toyota

$arabam->motoruCalistir(); // Çıktı: Motor Çalıştı!
$arabam->sur(); // Çıktı: Araba sürülüyor...

Güncellenen "Araba" sınıfında, "$renk" ve "$marka" öznitelikleri artık özel olarak bildirilir ve sınıfın dışından doğrudan erişim engellenir. Bunun yerine, niteliklerle etkileşime geçmek için genel alıcı ve ayarlayıcı yöntemleri ("getRenk()", "setRenk()", "getMarka()", "setMarka()") sağlıyoruz. Bu şekilde, nesnenin verilerine erişim üzerinde kontrol sağlarız.

Miras

Kalıtım, nesnelerin özellikleri ve davranışları diğer nesnelerden devralmasına izin veren, nesne yönelimli programlamada güçlü bir özelliktir. Kodun yeniden kullanılmasını sağlar ve sınıflar arasında hiyerarşik ilişkilerin oluşturulmasını destekler.

PHP'de kalıtım, "extends" anahtar kelimesi kullanılarak elde edilir. Kalıtımın nasıl çalıştığını anlamak için bir örnek ele alalım:

class Arac {
     protected $marka;
    
     public function __construct($marka) {
         $this->marka = $marka;
     }
    
     public function getMarka() {
         return $this->marka;
     }
    
     public function motoruCalistir() {
         echo "Motor Çalıştı!";
     }
}

class Araba extends Arac {
     private $renk;
    
     public function __construct($marka, $renk) {
         parent::__construct($marka);
         $this->renk = $renk;
     }
    
     public function getRenk() {
         return $this->renk;
     }
    
     public function sur () {
         echo "Araba sürülüyor...";
     }
}

Bu örnekte, marka özniteliğini tanımlayan ve ona erişmek için yöntemler sağlayan bir Arac temel sınıfımız var. Araba sınıfı, extends anahtar kelimesini kullanarak Arac sınıfını genişletir, böylece marka özniteliğini ve ilişkili yöntemlerini devralır.

Araba sınıfı ayrıca $renk adlı yeni bir özel öznitelik sunar ve getRenk() ve sur() gibi bir arabaya özgü ek fonksiyonlar sağlar.

Araba sınıfından bir nesne oluşturmak ve özelliklerine ve yöntemlerine erişmek için aşağıdakileri yapabiliriz:

$arabam = new Araba("Toyota", "Mavi");
echo $arabam->getMarka(); // Çıktı: Toyota
echo $arabam->getRenk(); // Çıktı: Mavi
$arabam->motoruCalistir(); // Çıktı: Motor Çalıştı!
$arabam->sur(); // Çıktı: Araba sürülüyor...

Kalıtımı kullanarak, Araba sınıfı getMarka() yöntemini Arac sınıfından devralır ve ona doğrudan Araba nesnesinden erişebiliriz.

Polimorfizm

Polimorfizm, farklı sınıflardaki nesnelerin ortak bir üst sınıfın nesneleri olarak ele alınmasına izin verir. Birden çok formu veya türü temsil etmek için tek bir arabirimin kullanılmasını sağlar.

PHP'de polimorfizm, yöntem geçersiz kılma veya yöntem aşırı yükleme yoluyla elde edilebilir. Her iki kavramı da inceleyelim:

Metodu Geçersiz Kılma:

Yöntem geçersiz kılma, bir alt sınıf, üst sınıfında zaten tanımlanmış olan bir yöntemin kendi uygulamasını sağladığında gerçekleşir. Alt sınıftaki yöntem, üst sınıftaki yöntemle aynı ada, aynı bağımsız değişkenlere ve aynı veya uyumlu bir dönüş türüne sahip olmalıdır.

class Hayvan {
    public function ses() {
        echo "Hayvanlar ses çıkarır.";
    }
}

class Kedi extends Hayvan {
    public function ses() {
        echo "Kedi miyavlar.";
    }
}

class Kopek extends Hayvan {
    public function ses() {
        echo "Köpek havlar.";
    }
}

$hayvan = new Hayvan();
$kedi = new Kedi();
$kopek = new Kopek();

$animal->ses(); // Çıktı: Hayvanlar ses çıkarır.
$cat->ses(); // Çıktı: Kedi miyavlar.
$dog->ses(); // Çıktı: Köpek havlar.

Yukarıdaki örnekte, Hayvan sınıfının ses() metodu vardır. Kedi ve Kopek sınıfları, Hayvan'dan miras alır ve ses() yönteminin kendi uygulamalarını sağlar. İlgili sınıfın bir nesnesinde ses() yöntemi çağrıldığında, o sınıftaki geçersiz kılınan yöntem yürütülür.

Yöntem Aşırı Yükleme:

Yöntem aşırı yüklemesi, aynı ada ancak farklı parametrelere sahip birden çok yöntemin bir sınıfta bulunmasına izin verir. PHP yerel olarak yöntem aşırı yüklemesini desteklemez, ancak "__call()" gibi sihirli yöntemler kullanılarak veya isteğe bağlı parametreler kullanılarak simüle edilebilir.

class HesapMakinesi {
    public function topla($sayi1, $sayi2) {
        return $sayi1 + $sayi2;
    }
    
    public function ucSayiTopla($sayi1, $sayi2, $sayi3) {
        return $sayi1 + $sayi2 + $sayi3;
    }
}

$makine = new HesapMakinesi();
echo $makine->topla(5, 10); // Çıktı: 15
echo $makine->ucSayiTopla(5, 10, 15); // Çıktı: 30

Bu örnekte, Hesap Makinesi sınıfının topla() adlı iki yöntemi vardır, ancak bunlar kabul ettikleri parametre sayısı bakımından farklılık gösterir. Farklı parametre listeleri sağlayarak, yöntem aşırı yüklemesine benzer işlevsellik elde edebiliriz. Yöntemler çağrılırken PHP, iletilen bağımsız değişkenlerin sayısına ve türüne göre uygun yöntemi otomatik olarak çözer.

Soyutlama

Soyutlama, karmaşık sistemleri daha küçük, daha yönetilebilir bileşenlere ayırarak basitleştirme sürecidir. Gereksiz ayrıntıları gizlerken bir nesnenin temel özelliklerini ve davranışını yakalamayı içerir.

PHP'de soyutlama, soyut sınıflar ve arayüzler kullanılarak gerçekleştirilebilir.

Soyut Dersler:

Soyut sınıflar, somutlaştırılamayan sınıflardır. Diğer sınıflar tarafından genişletilmeleri amaçlanmıştır. Soyut sınıflar hem soyut hem de soyut olmayan yöntemler içerebilir. Soyut yöntemler bir uygulama olmadan bildirilir ve somut (soyut olmayan) alt sınıflar tarafından uygulanmalıdır.

abstract class Sekil {
    abstract public function alanHesapla();
    
    public function detay() {
        echo "Bu bir şekildir.";
    }
}

class Dikdortgen extends Sekil {
    private $genislik;
    private $yukseklik;
    
    public function __construct($genislik, $yukseklik) {
        $this->genislik = $genislik;
        $this->yukseklik = $yukseklik;
    }
    
    public function alanHesapla() {
        return $this->genislik * $this->yukseklik;
    }
}

$dikdortgen = new Dikdortgen(5, 10);
echo $dikdortgen->alanHesapla(); // Çıktı: 50
$dikdortgen->detay(); // Çıktı: Bu bir şekildir.

Bu örnekte, Sekil sınıfı soyut olarak bildirilir ve soyut bir alanHesapla() yöntemi içerir. Dikdortgen sınıfı, Sekil sınıfını genişletir ve alanHesapla() yöntemi için bir uygulama sağlar. detay() yöntemi, soyut sınıftan miras alınır.

Arayüzler:

Arayüzler, bir sınıfın uygulaması gereken bir yöntem sözleşmesi tanımlar. Soyut sınıfların aksine, arabirimler herhangi bir yöntem uygulamasına sahip olamaz. Bir sınıf birden çok arabirim uygulayabilir, böylece birden çok sözleşmeyi destekler.

interface Arac {
    public function motoruCalistir();
    public function sur();
}

class Araba implements Arac {
    public function motoruCalistir() {
        echo "Motor Çalıştı!";
    }
    
    public function sur() {
        echo "Araba sürülüyor...";
    }
}

$arabam = new Araba();
$arabam->motoruCalistir(); // Çıktı: Motor Çalıştı!
$arabam->sur(); // Çıktı: Araba sürülüyor...

Bu örnekte, Arac arayüzü iki yöntem tanımlar: motoruCalistir() ve sur(). Araba sınıfı, Arac arayüzünü uygular ve böylece bu iki yöntemin uygulanmasını zorunlu kılar. Arabirimi uygulayarak, Araba sınıfı, arabirim tarafından tanımlanan beklenen işlevselliği sağlamasını garanti eder.

PHP Örnekleri

Nesne yönelimli programlama kavramlarını daha fazla açıklamak için, birkaç PHP örneğini inceleyelim.

Örnek 1: Banka Hesabı

class BankaHesabi {
    private $hesapNo;
    private $bakiye;
    
    public function __construct($hesapNo) {
        $this->hesapNo = $hesapNo;
        $this->bakiye = 0;
    }
    
    public function yatir($tutar) {
        $this->bakiye += $tutar;
    }
    
    public function withdraw($tutar) {
        if ($this->bakiye >= $tutar) {
            $this->bakiye -= $tutar;
        } else {
            echo "Yetersiz bakiye.";
        }
    }
    
    public function getBakiye() {
        return $this->bakiye;
    }
}

$hesap = new BankaHesabi("123456789");
$hesap->yatir(1000);
$hesap->cek(500);
echo $hesap->getBalance(); // Çıktı: 500

Bu örnekte, BankaHesabi sınıfı, para yatırma, para çekme ve bakiye sorgulama gibi özelliklere sahip bir banka hesabını temsil eder. Her banka hesabı nesnesi, benzersiz bir hesap numarasıyla oluşturulur ve 0 bakiyesiyle başlatılır. yatir() ve cek() yöntemleri, verilen tutara göre bakiyeyi günceller ve getBakiye() yöntemi, mevcut denge.

Örnek 2: Çalışan Yönetim Sistemi

class Calisan {
    private $isim;
    private $maas;
    
    public function __construct($isim, $maas) {
        $this->isim = $isim;
        $this->maas = $maas;
    }
    
    public function getIsim() {
        return $this->isim;
    }
    
    public function getMaas() {
        return $this->maas;
    }
    
    public function yillikMaasHesapla() {
        return $this->maas * 12;
    }
}

class Yonetici extends Calisan {
    private $bonus;
    
    public function __construct($isim, $maas, $bonus) {
        parent::__construct($isim, $maas);
        $this->bonus = $bonus;
    }
    
    public function getBonus() {
        return $this->bonus;
    }
    
    public function yillikMaasHesapla() {
        return ($this->getMaas() + $this->bonus) * 12;
    }
}

$calisan = new Calisan("John Doe", 5000);
echo $calisan->getIsim(); // Çıktı: John Doe
echo $calisan->getMaas(); // Çıktı: 5000
echo $calisan->yillikMaasHesapla(); // Çıktı: 60000

$yonetici = new Yonetici("Jane Smith", 7000, 2000);
echo $yonetici->getIsim(); // Çıktı: Jane Smith
echo $yonetici->getMaas(); // Çıktı: 7000
echo $yonetici->getBonus(); // Çıktı: 2000
echo $yonetici->yillikMaasHesapla(); // Çıktı: 96000

Bu örnekte, Calisan sınıfı, ad ve maaş gibi niteliklere sahip temel bir çalışanı temsil eder. getIsim() ve getMaas() yöntemleri bu özniteliklere erişim sağlar ve yillikMaasHesapla() yöntemi, aylık maaşı temel alarak yıllık maaşı hesaplar.

Yonetici sınıfı, Calisan sınıfını genişletir ve bir bonus özellik sunar. Prim tutarını hesaplamaya dahil etmek için yillikMaasHesapla() yöntemini geçersiz kılar.

Bu örnekler, nesne yönelimli programlama kavramlarının gerçek dünya senaryolarına nasıl uygulanabileceğini göstererek kod tasarımında esneklik ve modülerlik sağlar.