Fonksiyonlar uzun uzadıya kod doldurarak geliştiricinin ne kadar iyi kaç bin satır kod yazmış ilgisini oluşturması adına tuhaf diziler, çoğaltılmış belirsiz veri tiplerine sahip olmaması gerekir. Bir geliştirici açısından, tuvali tam doldurmak onun iyi ressam olduğu anlamına gelmez.

//HtmlUtil.java Karmaşık yazım biçimi multi if belirsiz tiplere en iyi örnek

public static String testableHtml(PageData pageData,
boolean includeSuiteSetup
   ) throws Exception {
     WikiPage wikiPage = pageData.getWikiPage();
     StringBuffer buffer = new StringBuffer();
     if (pageData.hasAttribute("Test")) {
       if (includeSuiteSetup) {
         WikiPage suiteSetup =
          PageCrawlerImpl.getInheritedPage(
                  SuiteResponder.SUITE_SETUP_NAME, wikiPage
          );
         if (suiteSetup != null) {
          WikiPagePath pagePath =
            suiteSetup.getPageCrawler().getFullPath(suiteSetup);
          String pagePathName = PathParser.render(pagePath);
          buffer.append("!include -setup .")
                .append(pagePathName)
                .append("\n");
         }
       }
       WikiPage setup = 
         PageCrawlerImpl.getInheritedPage("SetUp", wikiPage);
       if (setup != null) {
         WikiPagePath setupPath =
           wikiPage.getPageCrawler().getFullPath(setup);
         String setupPathName = PathParser.render(setupPath);”
“buffer.append("!include -setup .")
               .append(setupPathName)
               .append("\n");
       }
     }
     buffer.append(pageData.getContent());
     if (pageData.hasAttribute("Test")) {
       WikiPage teardown = 
         PageCrawlerImpl.getInheritedPage("TearDown", wikiPage);
       if (teardown != null) {
         WikiPagePath tearDownPath =
           wikiPage.getPageCrawler().getFullPath(teardown);
         String tearDownPathName = PathParser.render(tearDownPath);
         buffer.append("\n")
               .append("!include -teardown .")
               .append(tearDownPathName)
               .append("\n");
       }
        if (includeSuiteSetup) {
          WikiPage suiteTeardown =
            PageCrawlerImpl.getInheritedPage(
                    SuiteResponder.SUITE_TEARDOWN_NAME,
                    wikiPage
            );
          if (suiteTeardown != null) {
            WikiPagePath pagePath =
              suiteTeardown.getPageCrawler().getFullPath 
             (suiteTeardown);
            String pagePathName = PathParser.render(pagePath);
            buffer.append("!include -teardown .")
                  .append(pagePathName)
                  .append("\n");
          }
        }
      }
      pageData.setContent(buffer.toString());
      return pageData.getHtml();
   }
public static String renderPageWithSetupsAndTeardowns(
     PageData pageData, boolean isSuite
   ) throws Exception {
     boolean isTestPage = pageData.hasAttribute("Test");
     if (isTestPage) {
       WikiPage testPage = pageData.getWikiPage();
       StringBuffer newPageContent = new StringBuffer();
       includeSetupPages(testPage, newPageContent, isSuite);
       newPageContent.append(pageData.getContent());
       includeTeardownPages(testPage, newPageContent, isSuite);
       pageData.setContent(newPageContent.toString());
     }
    
     return pageData.getHtml();
   }

Küçültmek ve Sadeleştirmek

Kod geneline baktığınızda daha sadeleştirme derken az satırla yazmaktan bahsetmiyorum. Önemli olan kodun logic olarak işlevi oradaki bir dizi işlemden geçiyor olması. Ben bir dizi işleme aldığım kodu daha sade bir yapıya nasıl getirebilirim. Bu bir dizi işlem sonrası yeni bir özellik eklenmesi istenildiğinden nokta atışı alana varabiliyor olmanız ve oraya yeni işlem metodunuzu ekliyor olmanız gerekir.

public static String renderPageWithSetupsAndTeardowns(
   PageData pageData, boolean isSuite) throws Exception {
     if (isTestPage(pageData))
       includeSetupAndTeardownPages(pageData, isSuite);
       // eklenmesi gereken yeni bir metod varsa buraya ekleyebiliriz.
     return pageData.getHtml();
   }

“FUNCTIONS SHOULD DO ONE THING. THEY SHOULD DO IT WELL. THEY SHOULD DO IT ONLY.”

Robert C. Martin

Sonuç olarak fonksiyon yazmamızın en temel sebebi daha büyük bir kavramı ayrıştırmak. Karmaşık bloklar ve girintiler kodun görüntü kirliliği ile beraber anlaşılmasını zorlaştırır.

Switch İfadeleri

Kısa bir switch ifadesi yazmak zordur. Bir şey yapan switch de yazmak zordur. Doğaları gereği switch ifadeleri N tane şey yaparlar. Ve ne yazık ki kısmen, switch ifadelerinden her zaman kaçamıyoruz, ancak her switch ifadesinin alt seviye bir sınıfa gömüldüğünden ve tekrarlanmadığından emin olabiliriz. Elbette bunu polimorfizmle (çok biçimlilik) yaparız.

Şu koda bir bakalım, Employee ’nin tipine bağlı işlemlerden sadece birini gösteriyor:

public Money calculatePay(Employee e) throws InvalidEmployeeType {
    switch (e.type) {
        case COMMISSIONED:
            return calculateCommissionedPay(e);
        case HOURLY:
            return calculateHourlyPay(e);
        case SALARIED:
            return calculateSalariedPay(e);
        default:
            throw new InvalidEmployeeType(e.type);
    }
}

Bu fonksiyonla ilgili bir sürü problem var:

  1. Çok büyük ve Employee tipleri eklendikçe de büyümeye devam edecek.
  2. Kesinlikle birden fazla şey yapıyor.
  3. Tek Sorumluluk Kuralı’nı (SRP — Single Responsibility Rule) ihlal ediyor.Adından da anlaşılacağı gibi, bu ilke, her sınıfın bir sorumluluğu, tek bir amacı olması gerektiğini belirtir . Bu, bir sınıfın yalnızca bir iş yapacağı anlamına gelir
  4. Açık/Kapalı Prensibi’ni (OCP — Open/Closed Principle) de ihlal ediyor. Bu prensip; sürdürülebilir ve tekrar kullanılabilir yapıda kod yazmanın temelini oluşturur.

Bu kodun sorununun çözümü, switch ifadesini şu şekilde bir “Abstract Factory” tasarım deseni (design pattern) temeliyle çözebiliriz.

public abstract class Employee {
    public abstract boolean isPayday();
    public abstract Money calculatePay();
    public abstract void deliverPay(Money pay);
}
-----------------------------------------------------------------
public interface EmployeeFactory {
    public Employee makeEmployee(EmployeeRecord r) throws InvalidEmployeeType;
}
------------------------------------------------------------------
public class EmployeeFactoryImpl implements EmployeeFactory {
    public Employee makeEmployee(EmployeeRecord r) throws InvalidEmployeeType {
        switch (r.type) {
            case COMMISSIONED:
                return new CommissionedEmployee(r);
            case HOURLY:
                return new HourlyEmployee(r);
            case SALARIED:
                return new SalariedEmploye(r);
            default:
                throw new InvalidEmployeeType(r.type);
        }
    }
}

EmployeeFactoryImplEmployee türevlerinin uygun örneklerini (instances) oluşturmak için switch ifadesini kullanacak ve calculatePayisPayday ve deliverPay gibi çeşitli fonksiyonlar, Employee arayüzünden polimorfik olarak gönderilecektir.

Açıklayıcı İsimler Kullanın

Fonksiyonlar için iyi isimler seçmek gerekmektedir. Uzun isimler kullanmaktan kaçınmayın. Uzun tanımlayıcı isimler, kısa anlaşılmayan isimlerden veya uzun bir yorumdan daha iyidir.

Örneğin : isTestable ya da includeSetupAndTeardownPages gibi…

İsim seçmek için zaman harcamaktan korkmayın. Bir sürü farklı isim deneyin ve her biri ile kodu tekrar tekrar okuyun. Açıklayıcı isimler seçmek zihninizdeki tasarımı ortaya çıkaracak ve onu geliştirmenize yardım edecektir. Kodunuzla eşleşen en anlamlı kelimeleri seçmeye özen gösterin.

Fonksiyon Argümanları

Fonksiyonların argümanlar en az olacak biçimde olması gerekir. En ideal olan argüman kullanmamaktır. Eğer kullanılacaksa 3’ten fazla olmayacak biçimde olmalıdır. Argümanları fazla kullanmak test senaryoları yazmayı oldukça zorlaştırır. Argüman yoksa test yazmak kolaydır. Argüman sayısı arttıkça test yazmak zorlaşacaktır.

Boolean Argümanları

Boolean argümanları fonksiyon parametre olarak kullanamayız. Argüman true ise bir dizi bişey yap false ise bir dizi bir şey yapmak anlamı ifade ediyor. Fonksiyonu tamamen okumak durumunda bırakacaktır. Methodun netliğini karmaşıklaştırır.

// Method bu şekilde yazmak yerine 
render(boolean isSuite) 
// İkiye bölerek methodu netliğe kavuşturabiliriz.
renderForSuite()
renderForSingleTest()

İkili Argümanlı Fonksiyonlar

Bazı fonksiyonlarda ikili kullan sağlayabiliriz. Nokta çizmek için bir sınıfımız olduğunu düşünelim bu sınıf içinde method ikili argümana sahip olması olasıdır. Çünkü nokta iki argümanla sağlanabilir. Ancak kimi bazı kullanımlarda yanlış kullanımıda mevcut aşağıda olduğu gibi. Expected (beklenen) ve actual (gerçek) argümanları sıralamasını öğrenebilmek için kod üzerinde pratik yapmanız gerekir.

// nokta iki ayrı argümandan oluştuğundan bu yazım doğrudur.
Point p = new Point(0,0);
// kötü kullanım
assertEquals(expected, actual)

Nesne Argümanlar

Bir fonksiyon 2 ya da 3 argümandan fazlasına ihtiyaç duyuyorsa, bu argümanlardan birkaçı bir object’e dönüşebilir. Bu sayede okunabilirlik ve anlaşılabilirlik artacaktır.

Circle makeCircle(double x, double y, double radius);
// Point object oluşturmak gerekir.
Circle makeCircle(Point center, double radius); 

Argüman Listeleri

Bazen bir fonksiyona değişken sayıda argüman iletmek isteriz.

String.format(”%s worked %.2f hours.”, name, hours);
// Yukardaki list halini argüman listesi biçiminde dönüştürebiliriz.
format(String format, Object… args)
monad(Integer… args)