Yazılım Mimarileri ve Tasarım Desenleri Üzerine

Null Object Design Pattern Nedir? Nasıl Uygulanır?

Merhaba,

Bu içeriğimizde; C#, Java, Python vs. gibi OOP dillerinde null olabilen referansların getirisi olan olası hata durumlarıyla kontrol yapılarından ziyade daha tasarımsal olarak başa çıkabilmenin bir yolu olan Null Object Design Pattern‘ı inceliyor olacağız.

Null Object Design Pattern, bir nevi nesnelerin kullanımını ve davranışlarını açıklayan ve null durumlara istinaden ekstradan özel kontrollere gerek kalmaksızın yazılımın akışını sağlayan bir tasarım desenidir. Şimdi gelin bu tasarımın tam anatomisini inceleyelim.

Null Durumlarının Kritikliği?

Bir probleme odaklı hususi algoritma geliştirilirken yahut mimarisel çalışmalar yaparken genellikle kullanılan objelerin null olup olmadığı bizler için önemlidir! Misal vermemiz gerekirse;

    class Obj
    {
        //...
    }

yukarıda tanımlanan ‘Obj’ isimli model üzerinden aşağıdaki referansı değerlendirirsek eğer;

            Obj o = null;

görüldüğü üzere null değer barındırmaktadır. Yani bu referans özünde değersizdir. Daha teknik konuşmak gerekirse bellekte herhangi bir nesneyi işaretlememektedir. Dolayısıyla bizler bu referansa olan ihtiyaca istinaden öncelikli olarak bu referansın bir nesneyi işaret edip etmediğinden emin olmalı ardından kullanıma odaklanmalıyız. Öyle değil mi?

            Obj o = null;
            if (o != null)
            {
                //Eğer o referansı null değilse(yani bir nesneyi referans ediyorsa
                //o zaman işlem yap!
            }

Yukarıdakine koda bakarsanız, çalışma hayatınız boyunca benzerine çok şahit olduğunuza eminim diyebilirim. Peki bizler neden bu kontrolü yapıyoruz? diye sorduğumuzda cevabı basit! Olmayan yani teknik olarak null olan bir referans üzerinden herhangi bir işlem yapmaya çalışmak oldukça tutarsız bir durumdur. İşte bu durumda yazılımsal olarak hataya sebep olacaktır.

Mesela yukarıdaki kodu şöyle kullanırsak;

            Obj o = null;
            o.ToString();

null olan ‘o’ referansı üzerinden(yani olmayan bir nesne üzerinde) bir işlem yapılmaya çalışıldığı için hiç yabancı olmadığımız NullReferenceException hatasıyla karşılacağız. İşte bu yüzden öncelikle bu nesnenin null olup olmadığından emin olmamız gerekmektedir.

İyi de hoca! Bu referansın null olduğu zaten aşikar! dediğinizi duyar gibiyim. Evet, haliyle benim de örnek olsun diye bu referansı kullandığım aşikar 🙂 Keza burada dikkatinizi çekmeye çalıştığım husus bu tarz kontrollerin referanslarla çalışıldığı sürece kaçınılmaz olması üzerinedir. Nihayetinde gerçek dünyada elde edilen objeler genellikle metotlardan ya da propertylerden geldikleri için ilgili objelerin null olup olmamaları aşikarlıktan ziyade meçhul olmaktadır.

Null Object Design Pattern’ın Amacı Nedir?

Yukarıda konuştuğumuz gibi bir referans boş yani null olabilir. Ancak bizler bu null durumu, kodu if/else yapılarına boğmaksızın hiçbir şey yapmadan şeffaf bir şekilde nasıl ele alabiliriz sorusunu sorarak yola çıkacağız! Bunun için Null Object diye bir kavram üreteceğiz.

Null Object; bir referansın null durumunu ifade eden ve herhangi bir davranışı olmayan nötr bir nesnedir.

Böylece bir referansın boş olması durumlarında null yerine varsayılan olarak hiçbir davranış sergilemeyen Null Object’i kullanarak null‘dan kaynaklı olası hatalara karşı if/else kontrollerinden kodu arındırabilecek ve kısmi olarak null durumunu kapsülleyerek akışın temiz bir şekilde seyretmesini mümkün kılabileceğiz.

Null Object Design Pattern; değeri/nesnesi olmayan bir referansın null yerine herhangi bir davranışı olmayan nötr bir nesneyi(Null Object) temsil ederek kodu kontrol yapılarından arındırma stratejisidir.

Null Object Design Pattern Teoride Nasıl Kullanılır?

Bir nesneye Null Object Design Pattern’ı uygulayabilmek için öncelikle bu kontrolün yapılacağı türe ait işbirlikçi(collaborator) sınıflar oluşturmamız gerekmektedir. Neden işbirlikçi sınıf? diye sorarsanız eğer ilgili nesnenin null durumu yerine herhangi bir şey yapmayan ve ihtiyaç duyulmayan, o nesnenin null durumuna karşılık gelen ama yine de bellekte bir karşılığı olan hali olarak düşünebilirsiniz.

Misal, üstteki şemaya göz atarsanız eğer uygulamada kullanılacak esas nesne ‘RealOperation’ nesnesiyken, bu nesnenin null durumunda kullanılacak nesne ise ‘NullOperation’ nesnesidir. ‘RealOperation’ nesnesi içerisinde ‘request’ metodunda gerekli operasyonu/işi gerçekleştirmektedir. Lakin ‘NullOperation’ nesnesi ise birebir ‘RealOperation’ nesnesiyle aynı içeriğe sahip olsa dahi içerisindeki ‘request’ metodunda herhangi bir işlem yürütmemektedir. Yani boştur. Davranışı yoktur. Nötr davranış sergiler. Tek gayesi ‘RealOperation’ nesnesinin null durumunu ifade etmektir.

‘Client’ yapmış olduğu işlem sürecinde ‘RealOperation’ nesnesine ihtiyaç duyduğunda ve ilgili nesnenin üretilmediği ya da olmadığı durumda null yerine ‘NullOperation’ nesnesi elde edecek ve herhangi bir if/else kontrolüne gerek duymaksızın ‘request’ metodunu tetikleyebilecektir. Çünkü artık kâh ‘RealOperation’ kâh ‘NullOperation’ olmak üzere kesinlikle bir nesne gelecektir. Burada her iki nesnenin de client tarafından karşılanabilmesi için ‘AbstractOperation’ ismi verilen base class ile refere edildiğine dikkatinizi çekerim.

Şimdi gelin, Null Object Design Pattern’ın aktörlerini daha net kavramsallaştırarak ele alalım;

Null Object Design Pattern, null durumlarının işlenmesinin client’tan soyutlanması için kullanılır!

Null Object Design Pattern’ın Pratik Örneğini Yapalım

Evet, tüm bu tanımlamalara istinaden konuyu daha da netleştirecek bir örnek yapalım. Örneğimizde bir Repository‘den gelecek olan ‘Employee’ nesnesinin Null Object Design Pattern eşliğinde inşasını inceleyelim.

Görüldüğü üzere Null Object Design Pattern’ın uygulanması oldukça basit. Oluşturulan bu tasarımı aşağıdaki gibi kullandığımızda nasıl sonuç verdiğini gelin hep beraber inceleyelim.

        static void Main(string[] args)
        {
            EmployeeService employeeService = new(new());
            AbstractEmployee e = employeeService.GetEmployeeByName("Ahmet");
            e.GetFullName();
            e = employeeService.GetEmployeeByName("Mehmet");
            e.GetFullName();
            e = employeeService.GetEmployeeByName("Muiddin");
            e.GetFullName();
        }

Null Object Design Pattern Örnek Çalışma Çıktısı

Peki biz Null Object Design Pattern ile neyi sağlamış olduk? bu soruyu önceden cevaplandırmaya çalışmış olsam da sanırım tekrar sorduğunuzu duyar gibiyim… O halde yukarıda yapılan tasarımın olmadığı bir senaryoda client’ın nasıl bir kullanıma maruz kalacağını aşağıda zahiren görmenizde fayda olacaktır.
İşte, ortaya koyulan fark ilgili nesnenin null kontrolü yapılarak kodun aşırı şişirilmesini ortadan kaldırmış olmamızdır.

Null Object Design Pattern’da stratejisinde, nesnenin herhangi bir davranış sergilemeyen null halini oluşturmanın(Null Object), client açısından inşa edilen kodun daha az öngörülebilir bir durum ortaya çıkardığını düşünebilirsiniz. Nihayetinde client null yerine Null Object nesnesinin geleceğini bilemeyebilir yahut herhangi bir dökümandan beslenmediği sürece her daim null‘un gelmeyeceğinin garanti olacağını düşünemeyebilir. İşte böyle bir durumda if/else kontrolü uygulayarak algoritmanın seyrinde farklı hatalara sebebiyet verilebilir. İşte bu durum dökümantasyon ve domain uzmanlarıyla doğru etkileşim neticesinde atlatılabilecek nazik bir haldir. Onun dışında ekstradan yan etki yaratabileceğine dair bir durum yoktur.

C#’da Null Durumlarına Karşı Başka Hangi Refleksler Üretilebilir?

Null Object Design Pattern her ne kadar adında Design Pattern etiketini taşısada özünde bir tasarım deseni değil Refactoring tekniğidir!

Bazen Null Object’ler aşırı basit ve dummy data olarak görülebilmektedirler. Ancak gerçekte Null Object’ler diğer nesnelerle etkileşime girmeden ne yapılması gerektiğini her zaman tam olarak bildiğinden dolayı esasında çok akıllı nesnelerdir!

Genel Kurallar
Diğer Kalıplarla İlişkisi
Eleştiriler

Null Object Design Pattern, program akışında oluşan hataların normal program yürütmesi gibi görünmesine neden olacağından dolayı dikkat edilmesi gereken tasarımdır. Ayrıca önceki paragraflarda değinmeye çalışıldığı gibi client tarafından ilgili yapıya Null Object tasarımının uygulandığı bilinmeyebilir ve böylece if/else ile null kontrolü uygulanarak mantıksal hatalara sebebiyet verebilme ihtimali vardır. Haliyle bu yönlerinden dolayı hem dikkatli kullanılmalıdır hem de client açısından dökümantasyon ve domain expert’lerle ilişki oldukça önem kazanmaktadır. Ayrıca sadece boş kontrollerden kaçınmak ve kodu daha okunabilir kılmak için bu kalıbı uygulamaya özen gösterilmesi gerekmektedir.

Her şey bir nesnedir! Bir nesnenin yokluğu başka bir nesne tarafından modellenir.

Smalltalk İlkesi

İlgilenenlerin faydalanması dileğiyle…
Sonraki yazılarımda görüşmek üzere…
İyi çalışmalar…

Mobil sürümden çık