Tek Implementasyonlu Interface Yazmayı Bırakmak
Her sınıfa ihtimale karşı bir interface açmak: gerçek bir esneklik değil, kodu okumayı zorlaştıran spekülatif soyutlama.
Kısaca
Tek implementasyonlu interface, "interface'e karşı programla" prensibinin mekanik bir yanlış okumasıdır: gezinmeyi dolaylılaştırır, okumayı iki dosyaya böler ve olmayan bir esneklik vaat eder. Interface; birden çok gerçek implementasyon, dış bir sınır ya da kullanılan bir test dikişi varsa maliyetini hak eder. İkinci implementasyon geldiğinde extract interface birkaç dakikalık iştir.
Bir projede her sınıfın bir eşi vardı: UserService ve UserServiceInterface, OrderRepository ve OrderRepositoryInterface. Her interface’in tam olarak bir implementasyonu vardı. Klasör sayısı ikiye katlanmıştı; kazanılan esneklik sıfırdı.
Tek implementasyonlu interface, en yaygın spekülatif soyutlamadır — ve çoğu zaman yanlış anlaşılmış bir prensibin sonucudur.
“Interface’e karşı programla” yanlış okunuyor
“Program to an interface, not an implementation” prensibi yıllardır tekrarlanır. Ama prensipteki “interface” kelimesi, PHP’deki interface anahtar kelimesi değildir.
Prensibin söylediği şu: bir tipin iç ayrıntılarına değil, sözleşmesine bağımlı ol. Bir sınıfın public method’ları zaten onun sözleşmesidir — yani somut bir sınıf, kavramsal anlamda zaten bir “interface”tir. Bu prensibi “her sınıf için bir interface dosyası aç” diye okumak, bir tasarım fikrini mekanik bir törene çevirmektir.
Tek implementasyonlu interface ne maliyet getirir
Boş bir soyutlama bedava değildir:
- Gezinme dolaylılaşır. “Go to definition” sizi koda değil, interface’e götürür. Asıl mantığı görmek için bir adım daha atarsınız — her seferinde.
- Okuma zorlaşır. Bir davranışı anlamak için iki dosya açarsınız: ne söz verildiği ve ne yapıldığı. Tek bir somut sınıf, ikisini bir arada tutardı.
- Yanlış bir esneklik vaadi. Interface “burası değişebilir” der. Oysa değişmeyecektir; tek implementasyon yıllarca tek kalır.
Bu, gereğinden karmaşık sistem sinyallerinde saydığım kırmızı çizgilerden biridir.
Soyutlama ne zaman ücretsiz
Bir interface, gerçek bir işe yarıyorsa maliyetini hak eder. Gerçekten gerektiği yerler:
- Birden çok implementasyon gerçekten var. İki ödeme sağlayıcısı, iki depolama arka ucu — somut, bugün var olan iki şey.
- Gerçek bir dış sınır var. Uygulamanızın denetlemediği bir dünyayla — bir dış API, bir SDK — arasındaki sınır. Interface burada bir tampondur.
- Gerçekten kullanılan bir test dikişi var. Pahalı, yavaş, kontrol edemediğiniz bir bağımlılığın — bir ödeme ağ geçidinin — yerine testte sahte koymak için. Ama kendi veritabanınızı mock’lamak gibi, her şeyi bu gerekçeyle soyutlamayın.
Ortak nokta: üçü de bugün var olan bir ihtiyaç. “İhtimale karşı” değil.
Dikişi sonra açmak ucuz
En sık duyduğum itiraz: “ya ileride ikinci implementasyon gelirse?” Gelirse, interface’i o gün çıkarmak bir IDE refactor’ıdır — “extract interface”, birkaç dakika. İleride lazım olur kodunun faturası yazısındaki asimetri burada da geçerli: somut sınıftan interface çıkarmak kolaydır, gereksiz interface’i geri sökmek zordur.
İkinci implementasyon gelene kadar somut sınıfı doğrudan kullanın. Kod daha kısa, daha okunur ve tam olarak gerçekliği kadar karmaşık olur.
Tek implementasyonlu interface, çözdüğü bir problem olmadan eklenmiş bir katmandır. “İyi tasarım” gibi görünür; aslında ertelenebilecek bir kararın peşin ödenmiş hâlidir.
Interface’i ikinci implementasyon yazdırır — ihtimal değil.