Tasarım Deseni Serisi: Builder Design Pattern’i Anlamak

Herkese merhaba, bu yazımda sizlere Builder Design Pattern hakkında bilgi vermeye çalışacağım.

Aslında tasarım desenleri hakkında bir seri yayımlamak istiyorum bu yolla hem bilgilerimi tazeleyecek hem de sizlere yeni bilgiler vermeye çalışacağım.

Öncelikle bu konuya geçmeden önce bilmemiz gereken en önemli şey Builder Design Pattern’in hangi sorunlara karşılık olarak ortaya çıktığıdır.

Bu bilgi “Builder Design Pattern ne yapar?” sorusuna çok iyi bir yanıt olacaktır.

Builder Design Pattern adından da anlaşılacağı gibi bir nesneyi oluşturmaya yönelik ortaya çıkmış bir tasarım desenidir. Bu nedenle bu tasarım deseni Creational Design Patterns kategorisinde yer almaktadır.

Basit tanımıyla bu Pattern oldukça karmaşık olan ve oluşturması güç olan nesneleri daha kolay, daha kontrollü ve daha esnek bir şekilde oluşturmamıza imkân tanır.

Şimdi örneklerle Builder Design Pattern’in hangi konularda bize yardımcı olacağını anlatmaya çalışacağım.

Bu konuya değinirken aşağıdaki ProductModel sınıfını örnek olarak kullanacağım.

1.Karmaşık Nesne Oluşturma

Bazı sınıflar veya nesneler, çok sayıda parametre ya da yapılandırma seçeneği gerektiren karmaşık yapılara sahip olabilir.

Bu nesneleri sadece bir yapıcı metotla oluşturmak, kodun okunabilirliğini ve bakımını zorlaştırabilir.

Builder deseni, bu tür karmaşık nesneleri adım adım ve daha yönetilebilir bir şekilde oluşturabilmemizi sağlar.

Gerçek hayat projelerinde model olarak adlandırdığımız nesnelerde oldukça fazla parametre yer alabilmektedir. Yukarıdaki ProductModel sınıfını ele alırsak bu sınıftan bir nesne oluşturmaya çalıştığımızda bu sınıfın yapıcı metodunu (constructor) kullanabiliriz. Bu durumda;

Örneğin: Category parametresini es geçmek istersek yapıcı metodun parametreleri arasında Category kısmını bulmak zorunda kalacağız ayrıca bulsak bile kesinlikle bir veri atamak durumda kalacağız bu veri boş string ya da null gibi bir değer olacak.

Daha sonra modelimizde atama yapılmayan yerler arttıkça aşağıdaki gibi karmaşık bir görüntü ile karşı karşıya kalacağız.

Buna çözüm olarak birden fazla yapıcı metot kullanmak aklınızdan geçmiştir ancak bu durumda da çok fazla parametre olduğu için modelinizin içi yapıcı metotlardan ibaret olacaktır.

Alternatif olarak bu sorunu sınıflarınızı hiyerarşik olarak düzenleyerek çözmeyi düşünmüş olabilirsiniz.

Basitçe temel sınıfı genişletmek ProductModel parametrelerin tüm kombinasyonlarını kapsayacak bir dizi alt sınıf oluşturmaktır ama sonunda önemli sayıda alt sınıfa sahip olacağız. Yani aslında bir tarafta yapıcı metotlar artarken diğer taraf da ise alt sınıflarımız artacaktır.

Peki Builder Design Pattern bu sorunu nasıl çözecek?

Aşağıda bu durumu incelemek üzere bir örnek tasarladım.

Örneğe baktığımız zaman Abstract Class olarak tasarlanmış “BaseProductModelBuild” sınıfını görüyoruz bu sınıf örneğimizde builder olarak görev alıyor. Sınıfın görevi bize en son çıktı olarak verilen productModel nesnesini sunmak ve bu nesnenin parametrelerinin atanması için metot imzalarını oluşturmaktır. Bu Abstract sınıfı miras alan sınıflar ilgili metotları ezerek productModel nesnesinin parametrelerini atamaktadır ve atadıktan sonra kullanıcıya builder nesnesini dönmektedir. Builder nesnesini döndüğü için;

Örneğin: Önce SetName metodunu kullanarak name ataması yapıp dönen builder nesnesi ile birlikte SetDescription metodunu kullanarak description parametresini de atayabiliriz daha sonra yine builder döneceği için Category vs. parametlerini de atayarak ilerleyebileceğiz. Tüm atamalar bittikten sonra Build metodunu çağırarak productModel nesnemizi elde edeceğiz.

Aşağıya bu durumu özetleyecek bir kod örneği bırakıyorum.

Görüldüğü üzere buradan istediğim parametreyi istediğim sırada atayabilir ve atamalarımın tamamı bittiğinde Build metodunu çağırarak nesnemi yaratabilirim.

Karmaşık nesneleri nasıl oluşturabileceğimiz konusunda fikir verdiğimi düşünerek sonraki maddeye geçmek istiyorum. J

2. İmmutability (Değişmezlik) Desteği

Bazı durumlarda, oluşturulan nesnelerin değişmez (immutable) olması önemlidir. Builder deseni, bu gereksinimi karşılamak için de kullanılabilir. Immutable özelliğini sağlayacak Builder ile oluşturulan nesneler, bir kez yapılandırıldıktan sonra değiştirilemez. Bu da güvenlik ve tutarlılık açısından bize avantaj sağlamaktadır.

Bu yapı aslında az önce örneklediğimiz yapıdan farklı değildir.

Tek fark az önceki örnekteki gibi bir builder sınıfı oluşturulduğunda yapıcı metot içerisinde productModel nesnesi de oluşturuluyordu tıpkı aşağıda gösterildiği gibi.

Bu durumda builder ile birlikte productModel nesnesi de oluşturuluyor. Daha sonra ise parametreler productModel üzerinden atanıyordu ancak immutable bir yapı istiyorsak bu durumdan kaçınmalıyız. Çünkü önce productModel nesnemizi oluşturursak builder üzerinden sürekli olarak bu nesneye erişebiliriz.

Çözüm olarak şunu yapabiliriz; productModel nesnesini builder içerisinde saklamak yerine bu nesnenin parametrelerini saklayabiliriz. Böylece atama yaptığımız metotlar ile önce parametreleri atayıp daha sonra build metodu çalıştığında bu parametreler ile bir productModel nesnesi oluşturup kullanıcıya benzersiz bir productModel nesnesi sunabiliriz.

Bu konuyu bir örnekle daha rahat aktarabileceğimi düşünüyorum yine ProductModel sınıfımın üzerinden ilerleyeceğim.

Yukarıda görüldüğü üzere bir önceki durumda abstract sınıfımızın içerisinde productModel nesnesini tutuyorduk bu örnekte ise modelin parametrelerini tutuyoruz build metodunda ise parametrelerden yeni bir productModel oluşturup kullanıcıya yeni oluşturulmuş modeli sunuyoruz.

Bu konuya da değindikten sonra sonraki konumuza geçmek istiyorum.

3. Okunabilirlik ve Bakım Kolaylığı

Büyük ve karmaşık yapılara sahip nesneleri oluşturmak için Builder kullanmak, kodun daha okunabilir ve bakımını daha kolay hale getirir. Her bir oluşturma adımı açıkça tanımlanır ve gerektiğinde değiştirilebilir, bu da kodun daha modüler ve esnek olmasını sağlar.

Birçok yerde sıklıkla karşımıza çıkan model oluşturma ve parametre atama işlemleri aşağıda gösterildiği gibi olmaktadır.

Kodunuzun tamamında bu modeli çok fazla kez bu şekilde oluşturup parametre atamalarını yaptığınızı düşünün. Size productModel sınıfınızın Name özelliğinin artık Title olarak değiştirin dediğimde ne yapacaksınız?

Tabi ki kodunuzun tamamın Name olarak belirttiğiniz alanları Title olarak güncellemeniz gerekecektir.

Bu durum sizin için ciddi bir zaman kaybına yol açacaktır.

Builder Design Pattern kullanarak tasarlamış olsaydınız SetName metodunun içerisindeki productModel.Name kısmını productModel.Title olarak değiştirmeniz yeterli olacaktı. Builder Design Pattern ile parametreleri merkezi bir metot içerisinde atıyoruz. Böylelikle parametrelerimiz değiştiğinde merkezi metot içerisinde güncelleme yapmamız yetiyor, tıpkı aşağıdaki gibi.

Tek bir noktada yaptığım güncelleme tüm hatalarıma çözüm oldu. Tabi siz yine de metot isimlerini de güncellemeyi unutmayın.

Ayrıca merkezi yönetim sadece parametre değişikliklerinde işe yaramıyor. Parametre atarken rahatlıkla koşullandırmalar yapabilir ve merkezi olarak yönetebilirsiniz.

Hangi problemlere nasıl çözümler bulduğu hakkında bilgiler vermeye çalıştım tabii fazlası olduğuna da eminim. Bu yazıyı uzatmamak adına sonraki konuya geçmek istiyorum. J Sonraki konuda Builder Design Pattern içerisindeki aktörlere değinmek istiyorum.

Aslında yukarıdaki örneklerde bu aktörlerden bazılarını zaten kullanmış olduk ancak bir kere daha değinmenin faydalı olacağını düşünüyorum.

1. Director (Yönetici)

Director bizim oluşturduğumuz builder nesnelerini yönetmemize olanak tanır.

Örneğin: Bir modeli oluşturmak için birden fazla builder nesnesine sahip olabilirsiniz. Bu nesneler modellerinizi farklı şekilde oluşturuyor olabilirler. Bu durumda Director hangi builder nesnesi ile modelin oluşturulacağı kısmında bize yardımcı olur.

2. Builder (İnşaatçı)

Builder yukarıda verdiğimiz örneklerimizde de görüldüğü üzere modelin her bir parametresini oluşturmak için soyut metotlar veya işlevler içerir.

Bizim örneklerimizdeki BaseProductModelBuilder ve BaseImmutableProductModelBuilder Builder olarak görev almaktadır.

3. ConcreteBuilder (Somut İnşaatçı)

ConcreteBuilder sınıfları, belirli bir nesnenin oluşturulma sürecini uygular.

Aslında Builder ara yüzünü somutlaştırdığımız sınıflardır diyebiliriz.

Bizim örneklerimizdeki ProductModelBuilder ve ImmutableProductModelBuilder sınıfları ConcreteBuilder olarak görev almaktadır.

4. Product (Ürün)

Oluşturmayı hedeflediğimiz modeldir. Builder tarafından oluşturulup kullanıcıya sunulur.

Bizim örneklerimizdeki ProductModel sınıfları Product olarak görev almaktadır.

Aktörleri de inceledikten sonra özet olarak şunları söyleyebiliriz.

Builder tasarım deseni, bir nesnenin oluşturulmasının karmaşıklığını azaltır ve kullanıcıların yani istemcilerin bu nesnenin farklı varyasyonlarını oluşturmasını kolaylaştırır. Özellikle nesnenin yapısı karmaşıksa ya da çok sayıda yapılandırılabilir seçenek içeriyorsa, Builder tasarım deseni kodun daha temiz, okunabilir ve esnek olmasını sağlar.

Umarım bu yazı sizler için yararlı olmuştur, okuduğunuz için teşekkür ederim.

Bir yanıt yazın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir