PHP'de Nesne Yönelimli Programlama: Sınıf, Nesne ve Kapsülleme

PHP'de Nesne Yönelimli Programlama: Sınıf, Nesne ve Kapsülleme

6 dakika okuma

Nesne yönelimli programlamanın amacı kod satırlarını sınıflara bölmek değil, veriyi o veriyle çalışan davranışın yanında tutmaktır. Bir dizi ile onu işleyen beş ayrı fonksiyon arasında bağ yoktur; dizinin biçimi değiştiğinde beş fonksiyonun da güncellenmesi gerekir ve hangilerinin kaldığı yalnızca deneme yoluyla anlaşılır. Sınıf, bu bağı açıkça kurar.

Bu bölümde sınıf tanımı, yapıcı metot, görünürlük düzeyleri, değişmez nesneler ve sihirli metotlar ele alınıyor. Sınıfın ne zaman gerekli olduğu, bileşim ile kalıtım arasındaki tercih gibi tasarım soruları serinin sonraki bölümünde işleniyor.

Sınıf, nesne ve yapıcı metot

Sınıf bir kalıptır; nesne ise o kalıptan üretilmiş somut örnektir. Özellikler nesnenin durumunu, metotlar davranışını taşır. PHP 8 ile gelen yapıcı metot kısayolu, özellik tanımı ile atamayı tek yerde birleştirir.

PHP25 satır
final class Urun
{
    public function __construct(
        public readonly string $ad,
        public readonly int $fiyatKurus,
        private int $stok = 0,
    ) {}

    public function stokDus(int $adet): void
    {
        if ($adet > $this->stok) {
            throw new DomainException('Yetersiz stok');
        }

        $this->stok -= $adet;
    }

    public function stoktaVar(): bool
    {
        return $this->stok > 0;
    }
}

$urun = new Urun(ad: 'Kamp sandalyesi', fiyatKurus: 129900, stok: 12);
$urun->stokDus(2);

Örnekte stok bilgisi dışarıya kapalıdır. Azaltma işlemi yalnızca sınıfın kendi metodundan yapılabildiği için, stoğun eksiye düşmesini engelleyen kural tek bir yerde durur. Aynı kural özellik dışarı açık olsaydı, stoğu değiştiren her çağrı noktasında tekrarlanmak zorunda kalırdı.

Görünürlük düzeyleri

Üç düzey vardır. public her yerden, protected sınıf ve alt sınıflarından, private yalnızca tanımlandığı sınıftan erişilebilir. Varsayılan tercih private olmalıdır; bir üyenin dışarı açılması, o üyenin sözleşmenin parçası haline gelmesi demektir.

PHP14 satır
final class Hesap
{
    private array $hareketler = [];

    public function ekle(int $kurus): void
    {
        $this->hareketler[] = $kurus;
    }

    public function bakiye(): int
    {
        return array_sum($this->hareketler);
    }
}

PHP 8.4 ile okuma ve yazma için ayrı görünürlük tanımlanabiliyor. Aşağıdaki özellik dışarıdan okunur, ancak yalnızca sınıfın içinden değiştirilebilir. Bu yapı, okuyucu metot yazma ihtiyacını ortadan kaldırır.

PHP15 satır
final class Kullanici
{
    public function __construct(
        public private(set) string $ad,
    ) {}

    public function adDegistir(string $yeni): void
    {
        $this->ad = $yeni;
    }
}

$k = new Kullanici('Ali');
echo $k->ad;        // okunur
$k->ad = 'Veli';    // Error: Cannot modify private(set) property

Değişmez nesneler

readonly ile işaretlenen özellik yalnızca tanımlandığı sınıfın içinden bir kez atanabilir. Sonraki her yazma denemesi hata verir. Değişmezlik, bir nesnenin başka bir yerde beklenmedik biçimde değiştirilmesini engellediği için hata ayıklamayı belirgin biçimde kolaylaştırır.

PHP13 satır
final readonly class Adres      // tüm özellikleri readonly olur
{
    public function __construct(
        public string $sehir,
        public string $ilce,
        public string $posta,
    ) {}

    public function sehirDegistir(string $yeni): self
    {
        return new self($yeni, $this->ilce, $this->posta);   // yeni nesne üretilir
    }
}

Değişmez nesnelerde güncelleme, yeni bir nesne döndürerek yapılır. Bu yaklaşım ilk bakışta maliyetli görünse de nesne oluşturmak ucuz bir işlemdir ve kazandırdığı öngörülebilirlik çoğu durumda maliyetin çok üzerindedir.

Kimlik taşımayan, yalnızca değeriyle anlamlı olan kavramlar için değer nesnesi kullanılır: para, tarih aralığı, e-posta adresi. Doğrulama yapıcı metotta bir kez yapıldığında, o tipteki her nesnenin geçerli olduğu garanti altına alınır.

PHP14 satır
final readonly class Eposta
{
    public function __construct(public string $deger)
    {
        if (filter_var($deger, FILTER_VALIDATE_EMAIL) === false) {
            throw new InvalidArgumentException('Geçersiz e-posta adresi');
        }
    }
}

function bildirimGonder(Eposta $adres): void
{
    // buraya ulaşan her adres geçerlidir, ek denetim gerekmez
}

Nesne kopyalama ve karşılaştırma

Nesneler değişkenlere kopyalanmaz, tanıtıcı olarak taşınır. İki değişken aynı nesneyi gösterebilir ve birinden yapılan değişiklik diğerinde de görünür. Gerçek bir kopya gerekiyorsa clone kullanılır.

PHP14 satır
$a = new Sepet();
$b = $a;              // aynı nesne
$c = clone $a;        // yüzeysel kopya

// içeride nesne tutan alanlar da kopyalanmalıdır
final class Sepet
{
    private ArrayObject $satirlar;

    public function __clone(): void
    {
        $this->satirlar = clone $this->satirlar;   // derin kopya
    }
}

Karşılaştırmada == aynı sınıftan olup olmadıklarına ve özelliklerinin eşitliğine bakar, === ise tam olarak aynı nesne olup olmadıklarını sorgular. Değer nesnelerinde eşitlik genellikle == ile ya da sınıfa yazılan bir karşılaştırma metoduyla ifade edilir.

Sihirli metotlar

Belirli durumlarda PHP tarafından kendiliğinden çağrılan metotlar vardır. En yaygın kullanılanı, nesnenin metne çevrilmesini tanımlayan metottur.

PHP11 satır
final class Para
{
    public function __construct(private readonly int $kurus) {}

    public function __toString(): string
    {
        return number_format($this->kurus / 100, 2, ',', '.') . ' TL';
    }
}

echo new Para(129900);   // 1.299,00 TL

__get ve __set ile tanımsız özelliklere erişim yakalanabilir. Bu iki metot kolaylık sağlıyor gibi görünse de sınıfın hangi özellikleri taşıdığını koddan gizler, düzenleyicilerin tamamlama yapmasını engeller ve yazım hatalarını çalışma zamanına erteler. PHP 8.4 ile gelen özellik kancaları, hesaplanan değerler için çok daha güvenli bir yol sunar.

PHP13 satır
final class Kisi
{
    public function __construct(
        private string $ad,
        private string $soyad,
    ) {}

    public string $tamAd {
        get => $this->ad . ' ' . $this->soyad;
    }
}

echo (new Kisi('Ayşe', 'Demir'))->tamAd;   // Ayşe Demir

Nesne üretimini denetlemek

Yapıcı metodun aldığı parametreler her zaman elde hazır bulunmaz. Veritabanı satırından, form verisinden ya da API yanıtından nesne üretmek gerektiğinde dönüşümü çağrı noktasına bırakmak yerine adlandırılmış üretici metotlar kullanılır. Bu metotlar hem dönüşümü tek yerde toplar hem de adlarıyla niyeti anlatır.

PHP22 satır
final class Kayit
{
    private function __construct(
        public readonly int $id,
        public readonly string $baslik,
        public readonly DateTimeImmutable $olusturma,
    ) {}

    public static function satirdan(array $satir): self
    {
        return new self(
            id: (int) $satir['id'],
            baslik: (string) $satir['baslik'],
            olusturma: new DateTimeImmutable($satir['olusturma'], new DateTimeZone('UTC')),
        );
    }

    public static function yeni(string $baslik, Saat $saat): self
    {
        return new self(0, $baslik, $saat->simdi());
    }
}

Yapıcı metodun kapatılması, nesnenin yalnızca tanımlı yollardan üretilmesini sağlar. Böylece eksik ya da tutarsız bir nesnenin var olması dilin kuralıyla engellenir; her üretim yolu kendi doğrulamasını yapar.

Sınıf ne zaman gereksizdir

Her işlemin sınıfa dönüştürülmesi gerekmez. Tek bir girdi alıp tek bir çıktı üreten, durum tutmayan bir işlem için fonksiyon yeterlidir. Yalnızca özellik taşıyıp davranış içermeyen sınıflar da çoğu zaman gereksizdir; bu durumda davranışın nereye kaçtığı sorulmalıdır.

  • Sınıfta yalnızca alıcı ve atayıcı metotlar varsa, kurallar başka bir yerde tekrarlanıyor demektir.
  • Bir metot yalnızca parametrelerini kullanıp nesnenin durumuna hiç dokunmuyorsa, o metot sınıfa ait olmayabilir.
  • Adı yönetici, işleyici ya da yardımcı ile biten sınıflar genellikle birbirine ait olmayan işleri toplar.

Kontrol listesi

  • Özellikler varsayılan olarak private; dışarı açılan her üye bilinçli bir karar.
  • Yapıcı metot, nesneyi geçerli bir durumda üretiyor; eksik veriyle nesne oluşturulamıyor.
  • Değişmeyen alanlar readonly ile işaretli.
  • Doğrulama kuralları çağrı noktalarında değil, ilgili sınıfın içinde.
  • Nesne kopyalamada iç nesneler için __clone tanımlı.
  • __get ve __set yerine açık özellik ya da kanca kullanılıyor.

İlgili yazılar

PHP eğitim serisi

Bu yazı, temel sözdiziminden üretim ortamına kadar ilerleyen 25 bölümlük serinin bir parçası. Bölümler sırayla okunacak biçimde hazırlandı, ancak her biri tek başına da kullanılabilir.

Temeller

  1. Kurulum ve temel sözdizimi
  2. Döngüler ve akış kontrolü
  3. Diziler
  4. Fonksiyonlar
  5. String fonksiyonları
  6. Süper globaller
  7. Form işleme
  8. Dosya işlemleri

Dilin ayrıntıları

  1. Tip sistemi ve strict_types
  2. Hata ve istisna yönetimi
  3. Düzenli ifadeler
  4. Tarih ve saat işlemleri

Nesne yönelimli PHP

  1. Sınıf ve nesne (bu yazı)
  2. Kalıtım, arayüz ve trait
  3. Statik üyeler, sabitler ve enum
  4. Namespace, Composer ve autoload

Veri ve güvenlik

  1. PDO ile veritabanı işlemleri
  2. Oturum yönetimi ve kimlik doğrulama
  3. Güvenli çıktı ve XSS
  4. JSON ile veri değişimi

Dış dünya ve üretim

  1. cURL ile dış API çağırma
  2. Kendi REST API'ni yazmak
  3. PHPUnit ile birim test
  4. Hata ayıklama ve performans ölçümü
  5. Üretime alma ve güvenlik

Yorumlar

Bu form reCAPTCHA ile korunur; Google Gizlilik Politikası ve Hizmet Şartları geçerlidir.

İlk yorumu sen yap.

İlgili Yazılar