Geçersiz yorumlar en baştan zarar

Kodu açıklamak için yazılan herhangi bir yorumun zamanla geçersiz olması oldukça olasıdır. Zaten bu yüzden kod içerisine az ve öz yorum yazmak sanat yapmaya benzer. Zamanla geçersiz olabilecek yorumları önceden kestirip hiç yazmamaksa, öğrenilebilecek bir refleks gibidir.

Yazılan her yorum, açıklamaya çalıştığı kodun bakımı için harcanan zaman kadar bakım ister. Bu da aslında kod yorumlarını oldukça pahalı birer meta hâline getiriyor. Düşünmeden yazdığımız bir kod yorumunun, belli başlı güncellemelerden sonra geçersiz hâle geldiğini düşünün. Önümüzde iki seçenek olacak:

  1. Yorumun hâlâ geçerli olup olmadığını kontrol etmek, eğer geçersiz hâle gelmişse güncellemek veya gereksiz olduğunu düşünüp silmek.
  2. Yorumu güncellemeye ayıracağımız zamanın önemli olduğunu düşünmek ve olduğu gibi bırakmak.

Argh… İkinci seçeneği düşünmek bile mideme kramp girmesine sebep oluyor. İlk seçeneğin ise bize zaman kaybettireceği aşikar; halbuki daha ilk başta yaptığımız hatayı yapıp kolayca geçersiz olabilecek bir yorum yazmasaydık, ne boşa zaman harcamak zorunda kalacaktık, ne de kodumuzu artık geçersiz olan bir yorumla kirletmeye mecbur kalacaktık.

Bir yorum ne kadar yaşlıysa ve açıklamaya çalıştığı koddan ne kadar uzaksa, yanlış olma olasılığı da o kadar fazladır. Sebebi basit. Yazılımcıların, yorumların doğruluğunu korumaları pek gerçekçi değil.

Clean Code, Robert C. Martin

Örnek

Kolayca geçersiz hâle gelebilecek bir yoruma verilebilecek çokça örnek var; halbuki zor olan, geçerliliğini yitirmeyecek bir yorum örneği verebilmek. Yine de biz, kodumuzu kirleten bu tarz yorumlara bir örnek verelim.

Diyelim ki, kitap ayrıntılarını göstermek için bir sayfamız var. Bu sayfamızın linkini oluşturacak bir fonksiyon yazmaya karar veriyoruz. Fonksiyonu yazdığımız sıralarda veritabanımızda fazla kitabımız yok ve linkte yazar adı kullanmayı mantıklı buluyoruz:

<?php

class Book // Kitap
{
   /**
    * Yazar adını kullanarak kitap için bir link yarat.
    */
   public static function createUrl(string $author /* yazar */): string
   {
      return 'https://kodgrit.com/kitap/'.$author;
   }
}

Şu anda yorum açısından herhangi bir sıkıntımız bulunmuyor. Devam edelim.

Veritabanımıza yeni kitaplar ekliyoruz ve zamanla bir yazarın birden çok kitabının veritabanımızda yer aldığını görmeye başlıyoruz. Yani sadece yazar ismini kullanarak yarattığımız linkler, bizi artık istediğimiz kitaba götürmeyecek. Biz de yazar ismine ek olarak kitap ismini de linke eklemeye karar veriyoruz. Fakat bunu yaparken, fonksiyonun bir nevi dökümanı sayılan yorumu güncellemeyi unutuyoruz.

<?php

class Book // Kitap
{
   /**
    * Yazar adını kullanarak kitap için bir link yarat.
    */
   public static function createUrl(string $author /* yazar */, string $title /* başlık */): string
   {
      return 'https://kodgrit.com/kitap/'.$author.'/'.$title;
   }
}

Her ne kadar kullandığımız argümanlarla güncellemeyi unuttuğumuz yorum arasında farklılıklar olsa da, yorumumuz hâlâ çok kötü durumda değil.

Fakat sonrasında fark ediyoruz ki, veritabanımızdaki kitap isimleri zaten benzersiz. Hem yazar ismini, hem de kitap başlığını kullanarak linkler oluşturmamız, linkleri uzatmaktan başka işe yaramıyor. Biz de artık yazar ismini linklerde kullanmamaya karar veriyoruz. Tabii ki yorumu güncellemeyi yine unutuyoruz.

<?php

class Book // Kitap
{
   /**
    * Yazar adını kullanarak kitap için bir link yarat.
    */
   public static function createUrl(string $title /* başlık */): string
   {
      return 'https://kodgrit.com/kitap/'.$title;
   }
}

Şimdi ne olduğunu gördünüz mü? Yorumda yazana göre yazar adını kullanarak kitap için bir link yaratması gereken fonksiyon, yazar adını argüman olarak almıyor bile.

Kafamız karışıyor. Yazar adının nerde kullanıldığını görmek için fonksiyonun içeriğini okumaya başlıyoruz ama bir türlü bulamıyoruz. Fonksiyon içeriğinin örneğimizdeki gibi tek satır değil de çok daha uzun olduğunu düşünün. Yazar adını bulmak için, sonrasında da bulamayınca yaşayacağımız kafa karışıklığını atmak için harcayacağımız zamanı hayal edin.

Temizlik zamanı

Hatalı bir yorum, hiç yorum olmamasından çok daha zararlıdır.

Clean Code, Robert C. Martin

Daha en başta hataya davetiye çıkarmayı engelleyebilirdik. Fonksiyonumuza yorum olarak “Yazar adını kullanarak kitap için bir link yarat.” yazmak, değişmeye meyilli bir detayı açıklayıcı metin olarak kullanmak demek.

Böyle durumlarda en mantıklısı, detayı hiçbir şekilde açıklamamak. Açıklayıcı bir yoruma sahip olmayan fonksiyona bakan yazılımcı, linkin ne kullanılarak oluşturulduğunu fonksiyon argümanlarına bakarak zaten çözecektir.

<?php

class Book // Kitap
{
   public static function createUrl(string $author /* yazar */): string
   {
      return 'https://kodgrit.com/kitap/'.$author;
   }
}

Böylece, her değişiklikte sadece argüman ismi değişecek ve yorumun doğruluğunu sağlamak için ekstra zaman harcamak zorunda kalmayacağız. Ya da daha kötüsünü, yorumu güncellemeyi unuttuğumuzda yaşatacağımız kafa karışıklığını, en baştan engellemiş olacağız.

Kod kendini anlatmalı, yorumlar değil

Yazılım kariyerim boyunca birçok farklı geliştirici ile çalıştım, belki de hepsinin kendine özgü bir kod yorum tarzı vardı. Kimisi hiç yorum yazmazdı, kimisi ise tek satır kod için beş satır yorum yazardı. Ben de bu gam içerisinde değişe değişe kendi tarzımı oluşturdum. Bugün geldiğim nokta benim için hâlâ bir son durak sayılmaz; fakat belli bir yarar-zarar dengesini görebildiğimi düşünüyorum.

Kod içerisine yazılan yorumlar, biz yazılımcılara bakımını sağlamamız gereken ek satırlar yaratıyor. Bir kod satırını ya da bir fonksiyonu açıklamak için yorum yazdığımızda, gelecekte dönüp bu kodu güncellediğimizde o yorumu da güncellemek veya en azından güncellememiz gerekip gerekmediğini kontrol etmemiz gerekiyor. Bu da harcamamız gereken ekstra zaman anlamına geliyor. Yani, ekstra maliyet.

Öte yandan, eğer açıklanması gereken bir kod yazmışsak ve bu koda yorum eklememişsek, kaybedeceğimiz zamanın bir önceki duruma göre çok daha fazla olacağını neredeyse emin olarak söyleyebilirim. Gelecekte dönüp bu koda baktığımız zaman ne yaptığını anlayamayacağız. Kodu belki de satır satır okuyup ne yaptığını çözmeye çalışmamız gerekecek.

Halbuki temiz kod, temiz yorum gerektirir. Kod yazarken kullandığımız isimlendirmelerle kodun kendi kendini anlatabilmesini sağlamalı, yorumları ancak ve ancak kodun okuyucuya anlatamadığını anlatmak için kullanmalıyız.

Örnek

Gereksiz yorumlara verilebilecek sürüyle örnek var. Birkaçını listeleyelim:

<?php

$i++; // i'yi artır

/**
 * Argümanları toplar, çıktıyı döndürür
 */
public function t(int $x, int $y): int
{
   return $x + $y;
}

/**
 * @param int $x
 * @return int
 */
public function kare(int $x): int
{
   return $x * $x;
}

İlk kod parçasına yazdığımız yorumun saçmalığı aslında ortada. ++ sembolü yazılımcıların nerdeyse hepsinin çok iyi bildiği bir aritmetik işlem sembolüdür. Bu işlemi anlatmak için yorum yazmaya gerek yok.

İkinci kod parçasında bir fonksiyon tanımladık. Fonksiyon için de bir fonksiyon dökümanı yazdık; yani fonksiyonun ne yaptığını yorum olarak ekledik. Bu her ne kadar yararlı görünse de, aslında kodun kendini anlatmasını sağlayıp yorumları silmek bizim için daha faydalı olacak.

Üçüncü kod parçasında ise, fonksiyon ismi aşağı yukarı ne yapacağını anlatıyor. Bu nedenle herhangi bir yorum eklenmemiş. Fakat fonksiyon dökümanında girdi ve çıktılar yazılmış. Bazı yazılım takımları bunu kendi dinamiklerinde zorunlu tutsa da, bana göre oldukça gereksiz. Girdi ve çıktı tipleri fonksiyon imzasında (public function kare(int $x): int) zaten mevcut.

Temizlik zamanı

Yukarıda verdiğimiz örnekleri temizlediğimizde aşağıdaki gibi bir görünüm elde edeceğiz:

<?php

$i++;

public function topla(int $x, int $y): int
{
   return $x + $y;
}

public function kare(int $x): int
{
   return $x * $x;
}

Peki ya bir yorumun gerekli ya da gereksiz olduğunu nasıl anlarız? Bunun için uygulayabileceğimiz iki aşamalı bir yöntem var:

  1. Yorumladığımız kodu düzenlediğimizde ve değişken ile fonksiyon isimlendirmelerinde iyileştirmeler yaptığımızda kod kendini anlatır hâle gelebiliyor mu? Cevap evetse, yorumu sil gitsin!
  2. Eğer birinci aşamada yorumları elimine edemediysek, sırada yazdığımız yorumun bize ne anlattığına bakacağız. Kodun NE yaptığını değil NEDEN yaptığını anlatıyorsa, bu, gerekli bir yorum olabilir. Eğer yorumumuz NEDEN sorusuna cevap bulmuyorsa, ilk aşamaya gidip kodumuzda biraz daha iyileştirmeler yapmayı deneyebiliriz.

Kırık Pencereler Teorisi

Şehir merkezlerinde bazı binalar güzel ve temizken, bazıları da çürük enkazlara benzer. Neden? Suç ve kentsel yozlaşma alanlarında uzman bazı araştırmacılar büyüleyici bir tetik mekanizması keşfettiler. Öyle ki bu mekanizma temiz, zarar görmemiş ve dolu bir binayı çok kısa bir süre içerisinde sahipsiz bir harabeye çevirebiliyor.

Kırık bir pencere.

Azımsanmayacak bir süre içerisinde tamir edilmeyen kırık bir pencere, bina sakinlerinin içine terk edilmişlik hissiyatını işliyor. Bu hissiyat, kimsenin binayı umursamamasına sebep oluyor. Böylece başka bir pencere daha kırılıyor. İnsanlar etrafa çöp atmaya başlıyor. Grafitiler ortaya çıkıyor. Bina yapısına ciddi zararlar gelmeye başlıyor. Görece kısa bir süre içerisinde bina, sahibinin tamire yanaşma sınırının çok ötesinde zarar görüyor ve böylece yerleşmiş olan terk edilmişlik hissiyatı gerçeğe dönüşüyor.

The Pragmatic Programmer: From Journeyman to Master

Kırık Pencereler Teorisi, 1980’li yıllarda şehircilikle alakalı olarak ortaya atılmış ve çokça destek görmüş bir teori. Birkaç yıl önce bu teoriyi bir yazılım geliştirme kitabında gördüğümde önce şaşırdım. Fakat temiz ve sürdürülebilir kod yazmak benim için bir süper güç olduğunda bu teorinin yazılım alanında ne denli güçlü bir araç olabileceğini fark ettim.

Temiz Kod’a ulaşma yolunda attığımız her adım, aslında kırık bir pencereyi tamir etmeye benziyor. Fakat, eğer temiz olmayan bir koda sahipsek, Temiz Kod’a ulaşmak demek, aslında terk edilmiş bir şehri baştan aşağı yenilemek demek.

Bununla birlikte, eğer hâli hazırda çok da kirli olmayan bir koda sahipsek veya her geçen gün yazdığımız yeni kodun temiz olduğunu düşünüyorsak, Kırık Pencereler Teorisi’ni aklımızda tutmak bize kodumuzu kirletmemenin yolunu gösterebilir.

Çalışmayan bir fonksiyon mu gördük? Beklemeden düzelt!

Sınıflarımızdan (class) birinin içerisinde gereksiz yorumlar mı mevcut? Hemen sil gitsin!

Gözümüze ismi pek de iyi olmayan bir değişken mi çarptı? Dur ve değişkenin ismini iyileştir!

Tüm bunlar birer zaman kaybı gibi görünse de, aslında uzun vadede kodun daha fazla çalışmayan fonksiyonla, gereksiz yorumlarla ve kötü isimlendirilmiş değişkenlerle şişmesini engelleyecek ve sıkı durun: Bize aslında çok daha fazla zaman kazandıracak!

…Andy’nin korkunç derecede zengin olan arkadaşının hikayesine tanık olalım. Bu kişinin evi tertemiz ve çok güzeldi; içi paha biçilemez antikalarla ve sanat eserleriyle doluydu. Bir gün, salonundaki şömineye fazla yakın asılmış bir duvar halısı alev aldı. İtfaiye günü kurtarmak için alelacele eve vardı. Fakat itfaiyeciler büyük, kirli hortumlarını evin içine sürüklemeden önce — ki bu sırada alevler gitgide büyüyordu — giriş kapısıyla yangının çıkış noktasının arasına bir mat sermek için durdular.

İtfaiyeciler, halıyı kirletmek istememişlerdi.

Elbette bu oldukça ekstrem bir durum; fakat yazılımda olması gereken de bu. Kırık bir pencere — kötü tasarlanmış bir kod parçası, yazılımcıların proje boyunca beraber yaşamak zorunda olduğu yanlış bir yönetim kararı —, kodun bozulmaya başlaması için gerekli olan yegane şeydir. Eğer kendinizi birkaç kırık pencerenin bulunduğu bir projede çalışırken bulursanız, “kodun geri kalanı da zaten çöp gibi, ben de aynı şeyi yapacağım,” deme ihtimaliniz oldukça yüksek. Projenin o ana kadar kötüye gitmemesinin hiçbir önemi yok. “Kırık Pencereler Teorisi”nin ortaya çıkmasına sebep olan asıl deneyde, terk edilmiş bir arabaya bir hafta boyunca hiçbir şey olmadı. Ama tek bir camı kırıldıktan sonra, arabanın soyulması ve tepetaklak hâle gelmesi yalnızca birkaç saat aldı.

The Pragmatic Programmer: From Journeyman to Master

Benim bu teoriden çıkardığım ders şu: Kırık bir pencereyi gördüğüm an tamir etmezsem, asla Temiz Kod’a sahip olamam.

Dipnot: Yukarıdaki iki alıntıyı çok sevdiğim bir kitap olan The Pragmatic Programmer: From Journeyman to Master‘dan kendim çevirdim.

Argümanlar birer çıktı malzemesi değildir

Fonksiyonun aldığı bir argümanı değiştirerek çıktı olarak vermek, temiz kodlama mantığına aykırıdır. Kodunuzu okuyan birisi, doğal olarak, girdilerin girdi, çıktıların ise çıktı olmasını bekler. Kodlama konusunda bir şey bilmediğinizi düşünün: Bir girdinin aynı zamanda çıktı olmasını bekler miydiniz?

Elbette herhangi bir yazılımcının kodlama bildiğini varsayıyoruz. 🙂 Dolayısıyla da herhangi bir yazılımcı aradaki farkı bulabilir; fakat bu farkı bulurken, harcamaması gereken bir mental enerjiyi bu ayrımı yapmak için kullanır.

Temiz kodlamanın temelinde yatan ilkelerden biri, kodunuzu okuyan bir kimsenin bunu çokça mental enerji kullanmadan yapabilmesini sağlamaktır. Bu nedenle de argümanlar, yani girdiler ile çıktılar arasındaki farkı en başından ortaya koymak, kodun daha temiz olmasını sağlayacaktır.

Örnek

<?php

class BookService // Kitap Servisi
{
   // updateBookTitle: Kitap başlığını güncelle
   public static function updateBookTitle(Book $book, string $title): Book
   {
      $book->title = $title;
      return $book; // Başlığı güncellenmiş kitabı döndür
   }
}

Yukarıdaki örnekte, BookService adındaki yardımcı sınıfımızın (class) tek fonksiyonunu inceleyelim. Fonksiyonumuz updateBookTitle iki adet argüman alıyor: Bir adet Book elemanı $book ve kullanmamız için bir kitap başlığı, $title.

Bu fonksiyonda temiz olmayan başka şeyler de var; fakat yazımız gereği, sadece çıktı argümanını inceleyeceğiz. Fonksiyon imzasının sonunda görüldüğü üzere (: Book), çıktı olarak bir Book elemanı veriyor. Yani, girdi olarak verdiği kitabı güncelliyor ve her ne kadar kitap aynı olsa da, yeni bir Book elemanını çıktı olarak veriyor.

Peki bu yöntem neden kötü? Gelin bir de bu fonksiyonu kullanmaya kalkışalım:

<?php

// Bir kitap elemanına sahip olduğumuzu varsayalım.
// @var Book $book

// Elimizdeki kitabın başlığını güncelleyelim.
BookService::updateBookTitle($book, 'Sineklerin Tanrısı');

// $book elemanının başlığı güncellenmedi!
// Çünkü güncellenmiş kopya çıktı olarak veriliyor.
// Bizim de bu çıktıyı kaydetmemiz gerekiyor.

// Tekrar deneyelim.
// @var book $book2
$book2 = BookService::updateBookTitle($book, 'Sineklerin Tanrısı');

// Başlığı güncellenmiş kitap, $book2 değişkenine kaydedildi.
// Alternatif yöntem:
$book = BookService::updateBookTitle($book, 'Sineklerin Tanrısı');

// Başlığı güncellenmiş kitap, $book değişkeninin üzerine yazıldı.

Yukarıdaki kullanım örneğini adım adım takip ettiğimizde görüyoruz ki, bir argümanı çıktı olarak kullanmak, bize nihayetinde yeni bir değişken yaratmamızı ya da eski değişkenin üstüne yazmamızı diretiyor. Bu da eski değişkenin ($book) varlığının bir nevi işe yaramaz hâle gelmesine sebep oluyor. Yani kodu okurken, $book elemanının son durumunu da takip etmek zorunda kalıyoruz.

Temizlik zamanı

Çıktı argümanı kullanmak yerine, direkt olarak argümanın durumunu güncelleyebiliriz. Yani, güncelleme işlemini kapsayan bir Book fonksiyonu yazabiliriz:

<?php

class Book // Kitap
{
   public function updateTitle(string $title): void
   {
      // Başlığı güncelle
   }
}

Dolayısıyla, bu fonksiyonun kullanımı da oldukça kolay olacak:

<?php

// Bir kitap elemanına sahip olduğumuzu varsayalım.
// @var Book $book

// Elimizdeki kitabın başlığını güncelleyelim.
$book->updateTitle('Sineklerin Tanrısı');

İşte bu kadar kısa ve öz! Görüldüğü üzere, çıktı argümanı kullanmak aslında kodumuzu daha karmaşık ve uzun hâle getirmiş.

Ölü fonksiyonlara ihtiyacımız yok

Bir fonksiyon kullanılmıyorsa ölü demektir. Kullanılmayan bir fonksiyonu barındırmaksa kodu şişirmekten ve kalabalık yapmaktan başka bir işe yaramaz. Kodu şişiren her fonksiyon, bizim için çeşitli zamanlarda ekstra yük anlamına gelir:

  1. Eğer bir bug yakalamaya çalışıyorsak, kontrol etmemiz gereken bir fazla fonksiyon anlamına gelir.
  2. Eğer bir sınıfı (class) daha iyi hâle getirmek için yeniden yazıyorsak (refactor), değiştirmemiz gereken bir fazla fonksiyon anlamına gelir.
  3. Yapmamız gereken herhangi bir şey olmasa dahi, bakımını sağlamamız gereken bir fazla fonksiyon var demektir.

Sadece bir fonksiyon; ne zararı var, öyle değil mi? Değil.

Birincisi, ölü fonksiyonlar birer birer artar. Bunların icabına zamanında bakmazsak (bkz. Kırık Pencereler Teorisi), ortalık çöplüğe döner.

İkincisi ise, bir fonksiyon için harcanan her dakikayı, başka ve daha yararlı bir kod parçası için harcayabileceğimizi düşünün. Her dakikanın bize belirli bir maliyeti var; akıllıca kullanmamız bizim olduğu kadar çalıştığımız projenin ya da ürünün de yararına.

Örnek

Aşağıdaki örneği ele alalım:

<?php

class Book // Kitap
{
   public function doSomething(): void
   {
      // Bir şeyler yap
      $this->updateTitle("Kürk Mantolu Madonna");

      // Yazarı bilgilendirme; çünkü Sabahattin Ali hayatta değil.
   }

   private function updateTitle(string $title): void
   {
      // Başlığı güncelle
   }

   private function notifyAuthor(): void
   {
      // Yazarı bilgilendir
   }
}

Pekala, sırf ölü fonksiyonlara örnek verebilmek için böyle bir senaryo yarattım. 🙂

İlk olarak, Book sınıfında tanımladığımız iki tane private (gizli) fonksiyon var. Bu tarz fonksiyonlar kendi sınıfları dışından erişilemediği için, yukarıdaki örnekte hangi fonksiyonun kullanılıp hangisinin kullanılmadığını anlayabiliyoruz.

Senaryonun ikinci önemli noktası ise, yazar olarak sadece Sabahattin Ali’yi kullanmış olmam. Gerçek bir projede elbette tek bir yazardan daha fazlasını idare etmemiz gerekecektir. Fakat örneğimizin işe yarar olması için, şimdilik başka yazarımız olmadığını varsayalım.

Gördüğünüz üzere, Book sınıfında üç adet fonksiyon var. Gizli olmayan (public) fonksiyon diğer bir fonksiyon olan updateTitle‘ı kullanıyor. Fakat üçüncü fonksiyonumuz olan notifyAuthor kullanılmıyor.

Temizlik zamanı

Ortada kullanmadığımız bir fonksiyon var. Bu fonksiyonu gönül rahatlığıyla silebiliriz:

<?php

class Book // Kitap
{
   public function doSomething(): void
   {
      // Bir şeyler yap
      $this->updateTitle("Kürk Mantolu Madonna");
   }

   private function updateTitle(string $title): void
   {
      // Başlığı güncelle
   }
}

Kodumuz kısaldı. Book sınıfını çok daha rahat bir şekilde okuyabiliyoruz.

Peki ya daha sonra projemize daha fazla yazar eklemeye karar verirsek ve bu yazarlardan bazılarını bilgilendirmemiz gerekirse; o zaman ne olacak?

Her ne kadar fonksiyonu silmiş olsak da, modern bir versiyon kontrol sistemi (mesela git) kullandığımız için, versiyonlamayı kullanarak bu fonksiyona ulaşabilmemiz mümkün.

Bir versiyon kontrol sistemi kullanmıyor musunuz? Kullanmalısınız!

İşaret argümanları sadece kafa karıştırır

İngilizce’de flag argument denen işaret argümanları, özetle, fonksiyonun birden fazla işe yaradığını gösterir. Bir fonksiyonun birden fazla iş yapması, Single Responsibility Principle adıyla bilinen, Tek Sorumluluk İlkesi‘ne aykırıdır. Bu ilkeye aykırı olmasının kötü yanı, kısaca, bu fonksiyonu değiştirmeye kalktığımızda birden fazla çıktıyı kontrol etmek zorunda kalacak olmamızdır.

Aşağıdaki örnekte görülen fonksiyon, bir işaret argümanı içeriyor:

<?php

class Book // Kitap
{
   // updateTitle: Başlığı güncelle
   // notifyAuthor: Yazarı bilgilendir
   public function updateTitle(string $title, bool $notifyAuthor): void
   {
      $this->title = $title;
      if ($notifyAuthor === true) {
         // Yazara e-posta gönder.
      }
   }
}

Burdaki işaret argümanı $notifyAuthor. Fonksiyonumuz, bizden yazarı bilgilendirip bilgilendirmemek için bir direktif bekliyor. Yani, aslında bu fonksiyonun yaptığı iki iş var:

  1. Kitabın başlığını güncellemek
  2. Yazarı bu konuda bilgilendirmek

Fonksiyonumuzun iki iş yapmasının neden kötü olduğunu, Tek Sorumluluk İlkesi‘ni anlattığım başka bir yazımda anlatacağım.

Fakat burda $notifyAuthor işaret argümanını kullanmamız, fonksiyonun içinde bir if koşul cümleciği kullanmamıza sebep oldu. if cümlecikleri oldukça yararlı olsalar da, kodu daha karmaşık hâle getirirler; çünkü kullanıldıkları anda ortaya iki farklı olasılık çıkar. Üstelik iki tane koşul kullandığımızda bu olasılık dörde, dört tane kullandığımızda ise bu olasılık sayısı 16’ya yükselir. Sonuç olarak, koşul cümlecikleri, kodu okumayı oldukça zorlaştırır.

Temizlik zamanı

Peki, işaret argümanı kullanılmış bir fonksiyonu nasıl temizleriz? İşaret argümanları belli bir eyleme işaret ettiği için, genelde temizlemesi de kolay olur. 🙂 Yapmamız gereken şey, bu eylemi fonksiyon içinden çıkartarak yeni bir fonksiyon içerisine hapsetmek:

public function updateTitle(string $title): void
{
   // Başlığı güncelle
}

public function notifyAuthor(): void
{
   // Yazarı bilgilendir
}

Böylece, kitabın başlığını ne zaman güncellemek istersek, yazarı bilgilendiren fonksiyonu da ek olarak çağırıp çağırmamak tamamen bizim elimizde olacak. Üstelik şimdi, fonksiyonlarımızın tam olarak ne yaptıkları, fonksiyon isimlerinde doğru bir şekilde belirtildiği için, herhangi bir kafa karışıklığına da sebep olmayacak.

Az argüman, temiz fonksiyon

Fonksiyon geliştirirken yapılan hatalardan biri, fonksiyonu çokça argümanla donatmaktır. Fonksiyona çokça argüman eklemek, her ne kadar fonksiyonu çok işlevli gösterse de kodun kirlenmesine sebep olur; fazlaca argümana sahip bir fonksiyonu değiştirmeye çalıştığınız her seferde kodun işleyişini bozmanız çok olasıdır.

Gelin, şimdi aşağıdaki örnek üzerinden bol argümanlı bir fonksiyonu nasıl daha temiz hâle sokarız, görelim:

<?php

class Book // Kitap
{
   // title: Başlık
   // author: Yazar
   // pageNumber: Sayfa sayısı
   public function update(string $title, string $author, int $pageNumber): void
   {
      // ...
   }
}

Yukarıdaki fonksiyonun işi aslında çok basit: Kitap elemanının öğelerini güncelliyor. Bunu yapmak içinse başlık, yazar, ve sayfa sayısı bilgilerini istiyor.

Bu fonksiyonun nasıl problemlere yol açabileceğini tahmin edebiliyor musunuz? Diyelim ki aşağıdaki bilgilere sahip bir kitabımız var:

Kitap başlığı: Clean Code
Yazar adı: Robert Martin
Sayfa sayısı: 675

Karşılaşacağımız ilk problem, kitabın sadece başlığını güncellemek istediğimizde dahi, yazar ve sayfa sayısı bilgilerini de vermek zorunda olmamız. Diyelim ki kitap başlığını Türkçe “Temiz Kod” olacak şekilde güncellemek istedik:

$book->update("Temiz Kod", "Robert Martin", 675);

Gördüğünüz gibi, her ne kadar yazar adı ve sayfa sayısını değiştirmesek de, bu bilgileri de vermek zorunda kaldık.

İkincisi, bu fonksiyonu her çağırdığımızda hangi bilgiyi hangi sırayla vereceğimizi kontrol etmek için fonksiyonu tekrar tekrar okumak zorunda kalacağız. Yani argüman sırasını yanlış hatırlayıp, şöyle bir hataya düşmemiz mümkün:

$book->update("Robert Martin", "Temiz Kod", 675);

Şimdi de yaptığımız hata sayesinde kitabın başlığı “Robert Martin”, yazarı ise “Temiz Kod” oldu. 🙂

Temizlik zamanı

Yukarıda anlattığımız iki problemi de çözecek şekilde fonksiyonumuzu yeniden yapılandıracağız; bunun için de daha az argüman kullanacağız.

Daha az argüman kullanmak demek, fonksiyonun işlevselliğini kaybetmesi anlamına gelmiyor. Aksine, yukarıdaki fonksiyonu üç farklı fonksiyon şeklinde yazdığımızda hem olası kafa karışıklıklarından kurtulacağız, hem de gereksiz kod yazmamış olacağız.

<?php

class Book // Kitap
{
   public function updateTitle(string $title): void
   {
      // Kitabın başlığını güncelle.
   }

   public function updateAuthor(string $author): void
   {
      // Kitabın yazarını güncelle.
   }

   public function updatePageNumber(int $pageNumber): void
   {
      // Kitabın sayfa sayısını güncelle.
   }
}

Üç farklı argüman kullanmak yerine, fonksiyon sayısını artırarak tek argümanlı fonksiyonlara sahip olduk. Peki bize ne gibi yararları oldu?

Şimdi, kitap başlığını güncellemek istediğimizde kullanacağımız fonksiyon yalnızca şu olacak:

$book->updateTitle("Temiz Kod");

Böylece fonksiyonumuz gereksiz yere yazar adını ve sayfa sayısını güncellemek zorunda kalmayacak. Buna ek olarak, fonksiyon isimlerimizde neyi güncellediğimiz belirtildiği için, kitap başlığını güncellemek isterken yanlışlıkla yazar adını güncelleme ihtimalimiz da ortadan kalkacak.

Sonuç olarak, her ne kadar fonksiyon sayısını artırmış da olsak, daha okunabilir, temiz bir koda sahip olacağız.