Merhabalar,
Design Pattern serimizin ikinci yazısında çok fazla bilindiğini düşünmediğim Null Object Pattern’i sizlerle paylaşmak istedim. İsmine bakınca bir Creational pattern gibi gözükse de aslında Behavioral pattern grubunda bulunan bu pattern, bir nesnenin null olarak return edilmesi yerine basitçe o obje yerine geçebilecek, daha tutarlı ve hataya sebep olmayacak bir nesnenin döndürülmesini sağlamaktadır.
Yukarıdaki UML class diyagramı ile Null Object Pattern’in nasıl uygulanacağını ve bileşenlerinin neler olduğunu inceleyerek başlayalım.
Client: Null Object pattern yapısına bir nedenle bağımlılığı olan bir class’tır. Bağımlılığında bir fonksiyonaliteye ihtiyaç duymadığında Null Object pattern’in metotlarını kullanır.
AbstractObject: Bu abstract class, client class’ının kullanabileceği nesneler için bir temel oluşturmaktadır. Aynı zamanda Nullobject sınıfın temel sınıfıdır.
RealObject: Client class’ının kullanacağı fonksiyonel bağımlılığı sağlayan bir class’tır. Realobject class’ı AbstractObject class’ının somut bir alt sınıfı olarak tanımlanır.
NullObject: Client class tarafından kullanılabilecek AbstractObject class’ının somut ve hiçbir işlevselliği olmayan boş halidir. İşlevsel olmaması bu sınıfa bağlı olan Client class’ın ihtiyaçlarına göre değişiklik gösterebilir. Bu da bir nesne için birden fazla çeşitte NullObject’ler olabileceği anlamına gelir. Örneğin; bazı NullObject’ler kendine ait metotları işlevsel olmasa da client’ın beklentisi olan loglama işlemlerini yapabilir ya da client için anlamlı mesajlar dönebilir.
Bu pattern’de önemli nokta, bir obje null’da olduğunda sistemde sürekli bir if/else conditional kontrolü olmadan ya da bir nullobject exception almadan sistemin çalışmaya devam edebilmesini sağlamaktır. Pattern’imizin bileşenlerini ve temel mantığını öğrendikten sonra artık bir örnekle pekiştirmenin vakti geldi.
Genellikle bu pattern’i anlatmak için bir repository üzerinden find işlemi sonucunda kayıt olmaması gibi örnekler veriliyor. Aslında pattern’in mantığı için gayet anlaşılır.
Fakat bu pattern’i data kontrolü dışında alt yapıda kullanmak ile alakalı bir örnek vermek istiyorum. Diyelim ki sizin için kritik olmayan fakat null olması durumunda loglama yapmak istediğiniz bir DI servisiniz var.
Örnek için bir notification engine servisi olduğunu düşünelim. Bir işlem sonucunda bilgilendirme için bir mesaj gönderiyorsunuz. Bu mesaj, sizin için kritik değil, iletilmemesi bir business soruna sebep olmuyor. Ama provider bazen connection kuramadığı için null object döndürüyor ya da servis ayağa kalkamıyor.
Bu durum null object exception oluşturduğu ya da kod içerisinde kullanılan yerlerde null check’ler oluşturmasından dolayı uygulamanız üzerinde hatalara ya da kod tekrarlarına sebep oluyor. Bunu çözmek için de null object pattern uygulayacağız.
Öncelikle bir web api projesi açtıktan sonra ihtiyacımız olan abstract class ve interface’imizi oluşturdum. Örneğimiz bir notification engine olduğundan ve birden fazla notification tipi içerebileceğinden (SMS, e-posta, push gibi) daha generic bir yapı oluşturmaya çalıştım.
INotification: Interface’in generic olması için içerisine tüm tipler için sadece SendMessage metodunu ekledim.
NotificationAbstract (Abstract Object): Oluşturulmuş notification türüne göre SendMessage çağrılmasını sağladım. Böylece SMS, e-posta ya da nullobject nesnesi olsa da send metodu her zaman çağrılabilecek bir yapı kurulmuş oldu.
NotificationSmsService (Real Object): Client class tarafından çağırılarak ve yapılmak istenen notification gönderme işlemi gerçekleştirecek şekilde tasarladım. Provider notification tipine göre oluşturuluyor ve SendMessage ile notification gönderiliyor.
NotificationNullService (Null Object): Provider oluşturma işlemi sırasında bir hata alınarak real object oluşturulamadığı durumda devreye girerek sistemin hata almasını engelleyecek. Ayrıca SendMessage metodu içerisinde yapılan işlemleri loglayarak ya da retry mekanizması gibi işlemleri bu metot içerisinde yapabiliriz. Bu sayede bu metot ile sistemin hata almasını engellemiş oluyoruz.
NotificationFactory (Factory Method): Notification servislerini DI ile ayağa kaldıracağım için ve birden fazla türde notification tipi olmasından dolayı Null Object pattern ile birlikte basit bir Factory pattern yapısı oluşturdum. Bu metot ile uygulama, hangi provider’ları oluşturacağını ve bu provider’ların creation anında bir hata alması durumunda yerlerine geçecek nullobject servislerini belirlemektedir. Bağımlılık factory class’ında olduğu için bizim yapımızda bu kısma Client Class diyebiliriz.
Factory pattern detayları için bu bağlantıyı inceleyebilirsiniz.
Buradaki senaryomuzda uygulama ayağa kalkarken notification provider oluşturmaya çalışıyor fakat create esnasında hata alarak bunun yerine nullobject pattern nesnesini oluşturarak devam ediyor.
Ardından send notification işlemi için bir endpoint oluşturdum. Bu endpoint çalıştığında provider real bir object olmasa bile işlemi gerçekleşecek ve null object olması durumunda aşağıdaki gibi bir log kaydını sistemde tutulacaktır.
Uygulamanın örnek kodlarını linkinden indirebilirsiniz.
Umarım faydalı olmuştur. Bir sonraki yazımızda görüşmek üzere 😊
Referanslar
https://dev.to/avraammavridis/null-object-pattern-5bod
https://www.geeksforgeeks.org/null-object-design-pattern/