İçeriğe geç
Muhammet Şafak
en
Soran: Kaan Cevaplandı:

readonly value object'lerimde "withX" tarzı kopyalama metotlarını nasıl temiz yazarım?


Soru

Domain katmanımda para (Money) ve adres (Address) gibi value object'lerim var ve bunları PHP 8.2'nin `readonly` özelliğiyle immutable yaptım. Amacım nesne bir kez kurulduktan sonra hiç değişmemesi. Ama immutable kopyalar üretmeye çalışırken sürekli "Cannot modify readonly property" hatası alıyorum. Örneğin bir `withAmount()` metodunda property'yi güncellemeye çalışıyorum ve patlıyor. Bu "withX" tarzı kopyalama metotlarını temiz ve kendini tekrar etmeden nasıl yazarım?

Cevap

Kısa cevap: readonly’de mutasyon yoktur; kopyalarken property’yi değiştirmeye çalışmak yerine yeni bir instance dönmelisiniz. Aldığınız hata tam da bunu söylüyor — $this->amount = ... yasaktır, çünkü readonly property yalnızca kurulduğu scope’ta bir kez set edilebilir.

  1. Kök neden. readonly property’yi ilk atamadan sonra clone içinde bile (sıradan bir metotta) yeniden yazamazsınız; runtime bunu bilinçli olarak engeller. Yani withX’in içinde $this’i değiştirmek imkânsız. (Küçük bir düzeltme: readonly PHP 8.2 değil, PHP 8.1 ile geldi.)
  2. Taşınabilir ve temiz desen: new self + named arguments. Her withX, constructor’ı yeni değerlerle çağırır; böylece tüm invariant’lar tekrar doğrulanır. PHP 8.1’den beri her yerde çalışır:
    public function withAmount(int $amount): static
    {
        return new self(amount: $amount, currency: $this->currency);
    }
  3. Alan çoğaldıkça named arguments tekrarı kırar. Çok alanlı bir VO’da her withX’te sadece değişeni yazar, gerisini $this->... ile taşırsınız; named arguments sıraya bağımlılığı ortadan kaldırır ve okunurluğu korur.
  4. PHP 8.3+: __clone yalnızca deep-clone içindir. 8.3 ile readonly property’leri __clone içinde yeniden başlatabilirsiniz, ama bu withX’e dışarıdan yeni bir değer taşımaz; asıl amacı iç içe nesneleri (örneğin bir DateTimeImmutable) derin kopyalamaktır. withX’i bununla çözmeye çalışmayın.
  5. PHP 8.5: wither için tam bu var. 8.5, kopyalarken property override etmenizi sağlar; wither deseni tek satıra iner:
    public function withAmount(int $amount): static
    {
        return clone($this, ['amount' => $amount]);
    }
  6. Doğrulamayı unutmayın. new self her kopyayı constructor’dan geçirdiği için doğrulamayı (negatif tutar, geçersiz currency) bedavaya alırsınız. clone($this, [...]) ise constructor’ı çağırmaz; invariant kontrolünüz varsa clone yolunda onu elle tetiklemeniz gerekir.

Sonuç: Ben olsam PHP 8.2/8.3’te new self + named arguments ile giderdim — hem taşınabilir hem de her kopyayı doğrulamadan geçiriyor. 8.5’e geçtiyseniz ve doğrulama basitse clone($this, [...]) daha zarif; ama invariant’larınız katıysa, o güveni kaybetmemek için ben yine constructor tabanlı yolu tercih ederdim.

İlgili Yazılar

Etiketler: #PHP#OOP
Uzmanlık: PHP Geliştirici
Paylaş:

Yorumlar

Yorum yapmak için GitHub hesabınızla giriş yapmanız yeterli. Yorumlar GitHub Discussions üzerinde saklanır.

Diğer Sorular

Tüm sorular

Sitede Ara

Yazı, proje ve sayfalarda arama yapmak için yazmaya başlayın.

Esc ile kapat Pagefind ile güçlendirildi