Nesne Yönelimli Programlama: Sınıf, Arayüz ve Soyutlama

Nesne Yönelimli Programlama: Sınıf, Arayüz ve Soyutlama

3 dakika okuma

Nesne yönelimli programlama, veriyi ve o veriyi işleyen davranışı bir arada tutar. Kazancı, ilgili parçaların tek yerde toplanması ve dış dünyaya yalnızca gerekli olanın açılmasıdır. Ancak her sorun bu modele uygun değildir.

Girdisini alıp sonucunu döndüren, durum tutmayan işler için serbest fonksiyon hem daha basit hem daha test edilebilirdir. Sınıf, ortak bir durumu paylaşan davranışlar olduğunda anlam kazanır.

Sınıf ne zaman gerekir?

Pratik gösterge, aynı parametrelerin birden fazla fonksiyonda tekrar etmesidir. Bu parametreler aslında bir nesnenin durumudur ve sınıfa taşındığında imzalar sadeleşir:

PHP15 satır
declare(strict_types=1);

final class SiparisServisi
{
    public function __construct(
        private PDO $db,
        private LoggerInterface $gunluk,
    ) {}

    public function olustur(array $sepet, int $kullaniciId): int
    {
        // $this->db ve $this->gunluk her metotta hazır
        return 0;
    }
}

Yapıcı içinde özellik tanımı, PHP 8 ile gelen bir kısaltmadır. Aynı ad üç kez yazılmadığı için sınıfların bağımlılık listesi tek bakışta okunur hale gelir.

Kapsülleme

Kapsülleme, nesnenin iç durumunun dışarıdan doğrudan değiştirilememesidir. Tüm özellikleri açık bırakmak, sınıfı üzerinde metot bulunan bir diziye indirger ve tutarlılık denetimini imkansız kılar.

Değişmez olması gereken alanlar readonly olarak işaretlenir. Böylece nesne kurulduktan sonra o alanın değiştirilmesi çalışma anında engellenir:

PHP16 satır
final class Para
{
    public function __construct(
        public readonly int $kurus,
        public readonly string $birim,
    ) {
        if ($kurus < 0) {
            throw new InvalidArgumentException('Tutar negatif olamaz');
        }
    }

    public function ekle(self $diger): self
    {
        return new self($this->kurus + $diger->kurus, $this->birim);
    }
}

Doğrulamanın yapıcıda yapılması, geçersiz durumda nesnenin hiç oluşmamasını sağlar. Böylece kodun geri kalanı nesnenin geçerli olduğunu varsayabilir.

Arayüz ve bağımlılık yönü

Arayüz, bir davranışın sözleşmesini gerçekleştiriminden ayırır. Uygulama kodu somut sınıfa değil arayüze bağlandığında, gerçekleştirimin değişmesi çağıran tarafı etkilemez:

PHP12 satır
interface BildirimGonderici
{
    public function gonder(string $alici, string $mesaj): bool;
}

final class EpostaGonderici implements BildirimGonderici { /* ... */ }
final class SmsGonderici implements BildirimGonderici { /* ... */ }

final class SiparisBildirimi
{
    public function __construct(private BildirimGonderici $gonderici) {}
}

Bu yapının test tarafındaki karşılığı da önemlidir. Arayüz sayesinde testte gerçek eposta gönderimi yerine sahte bir gerçekleştirim verilebilir ve test dış servise bağımlı kalmaz.

Kalıtım yerine bileşim

Kalıtım, alt sınıfı üst sınıfın iç yapısına bağlar. Üst sınıfta yapılan değişiklik tüm alt sınıflara yayılır ve zamanla derin hiyerarşiler ortaya çıkar. Çoğu durumda ihtiyaç duyulan şey yeniden kullanımdır ve bu, nesneyi içeride tutarak da sağlanabilir.

Kalıtım yalnızca gerçek bir alt tür ilişkisi varsa kullanılmalıdır: alt sınıf, üst sınıfın geçtiği her yerde sorunsuz kullanılabiliyorsa ilişki doğrudur. Yalnızca kod paylaşmak için kurulan kalıtım, ilk değişiklikte sorun çıkarır.

Erken soyutlamanın maliyeti

Tek gerçekleştirimi olan arayüzler, gelecekte gerekebilir düşüncesiyle yazılan soyut sınıflar ve her sınıf için otomatik üretilen fabrikalar, çözdüğünden fazla karmaşıklık üretir. Okuyan kişi basit bir işlemi izlemek için beş dosya arasında dolaşmak zorunda kalır.

Uygulanabilir kural şudur: soyutlama, ikinci gerçek gereksinim ortaya çıktığında eklenir. İlk gerçekleştirimde doğrudan sınıf kullanmak, sonradan arayüz çıkarmayı zorlaştırmaz.

PHP 8 ile gelen araçlar

Sabit değer kümeleri için enum kullanmak, sınıf sabitlerine göre çok daha güvenlidir. Beklenen değer kümesi tip sistemine taşındığı için geçersiz değer hiç geçemez:

PHP13 satır
enum SiparisDurumu: string
{
    case Bekliyor = 'bekliyor';
    case Hazirlaniyor = 'hazirlaniyor';
    case Tamamlandi = 'tamamlandi';

    public function iptalEdilebilir(): bool
    {
        return $this !== self::Tamamlandi;
    }
}

function durumGuncelle(SiparisDurumu $durum): void { /* ... */ }

Enum içine davranış da yazılabildiği için, duruma bağlı kurallar tek yerde toplanır. Bu kuralın kod içine dağılmış koşullarla tekrarlanmasına gerek kalmaz.

Yapıcı içinde tanımlanan salt okunur özellikler, veri taşıyan sınıfları da sadeleştirir. Getter ve setter yazmadan, değeri korunan küçük nesneler oluşturmak mümkün hale gelir. Böylece veri yapıları hem güvenli hem kısa kalır.

Kontrol listesi

  • Durum paylaşılmıyorsa fonksiyon yeterlidir.
  • Özellikler dışarıya açılmaz, değişmezler readonly işaretlenir.
  • Doğrulama yapıcıda yapılır, geçersiz nesne hiç oluşmaz.
  • Bağımlılıklar arayüz üzerinden alınır.
  • Kod paylaşımı için kalıtım kurulmaz, bileşim tercih edilir.
  • Soyutlama ikinci gereksinim doğduğunda eklenir.

İlgili yazılar

Yorumlar

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

İlk yorumu sen yap.

İlgili Yazılar