Randevusuna yetişmek için apar topar evinden çıkarak metrobüs durağına koştu. Birkaç durak sonrasında inmek için bindiği metrobüste ki yoğunluğu şaşkınlıkla izliyordu. Kapasitesinin üzerinde insan yığmaya ne gerek vardı? Bu insanlar neler hissediyorlar? İnsanların yüzlerinde ki mutsuzluğu bir süre izledikten sonra metrobüsten indi. Adını ilk defa duyduğu kafeteryaya giderken yine bir kalabalığın ortasında buldu kendini. Birbirlerine çarpanlar, fotoğraf çekilenler, bağrış çağrışın eksik olmadığı yolda gözleri tabelalardan ayrılmıyordu. Sonunda bulduğu kafeteryanın önünde ise kimi dışarıda içeriden yer boşalmasını bekliyor kimi de birini arayan gözlerle içeriyi gözlemliyordu. Aynı bakışlarla içeriyi süzdükten sonra kendisine sallanan eli görmesiyle beraber içeriye adımını attı.
Nazım Hikmet Ran’ın dizelerinin yer aldığı görselle beraber ufak bir hikaye ile giriş yapmış olduk. Konumuz kalabalık mı gerçekten yoksa tasarım şablonları mı? 🙂 Builder tasarım şablonu sınıflarımızı oluşturmak için kullandığımız parametrelerin karmaşıklığını azaltarak daha sade ve okunabilir düzeyde olmasını sağlar. Aşağıda ki kodları incelediğimizde artan parametreler nedeniyle nesnelerin oluşumu sırasında biz yazılımcılara neyin nereye geldiği konusunda zorluk çıkartmaktadır. Yukarıda ki hikayede bunu açıklar. Kapasitenin üzerinde yolcu alan metrobüs ile kafeteryanın içinde ki arkadaşımızı bulmak için dışarıdan doğru bir süre içeriyi gözlemlememiz karmaşanın olduğu yerde bize bir takım zorlukların çıktığını anlatmaktadır. Artık konumuza teknik düzeyde giriş yapalım.
Bazen sınıflarımızın yapıcı(constructor) metotlarını oluştururken parametre sayısı farklı senaryolar nedeniyle artabilir. Senaryoya göre parametrelere ya null değer veririz ya da senaryoya göre yeni yapıcı metotlar oluştururuz. Mutlu mesut kodumuzu geliştirip devreye alır ve üzerimizde ki sorumluluğunu tamamlamış oluruz. Bir süre sonra yeni bir geliştirme ihtiyacı doğar ve yeni bir parametre daha ekleriz. Yarın bir gün birileri daha gelir yeni parametreler ekler işin içinden çıkamaz kendi ihtiyacına göre constructor oluşturur ve kod alır başını gider. Bu metodu kullanacak yazılımcıların vay haline! Çünkü hangi parametre nerede olacaksa takip edip sırayla parametrelerin değerlerini verir. Hadi bu kötü senaryoyu kodlayalım.
Alışveriş sitesi için telefonlara filtreleme yapılsın isteniyor. İlk ihtiyaca göre brand, model, year ve hasGprs özelliklerine göre filtreleneceği söyleniyor. Hemen kodluyoruz.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
public class PhoneFilter { private String brand; private String model; private String year; private boolean hasGprs; public PhoneFilter(String brand) { this.brand = brand; } public PhoneFilter(String brand, String model) { this.brand = brand; this.model = model; } public PhoneFilter(String brand, String model, String year) { this.brand = brand; this.model = model; this.year = year; } public PhoneFilter(String brand, String model, String year, boolean hasGprs) { this.brand = brand; this.model = model; this.year = year; this.hasGprs = hasGprs; } //getter //setter } |
Aradan zaman geldi geçti. Telefonlarda gelişmeler oldu ve kamera özelliği geldi. Tabi o zamanlar selfie yok. Bu geliştirmeyle de artık başka yazılımcı ilgileniyor. Hemen bugün geliştirme yapmamız gerekiyor telefon filtrelerine hasCamera özelliği ekleyeceğiz, denilince oda aşağıda geliştirmeyi yapıyor.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
public class PhoneFilter { //fields... private boolean hasCamera; //constructors... public PhoneFilter(String brand, String model, String year, boolean hasGprs, boolean hasCamera) { this.brand = brand; this.model = model; this.year = year; this.hasGprs = hasGprs; this.hasCamera = hasCamera; } //getter //setter } |
hasCamera nesnesini ekledikten sonra, yeni bir contructor eklemek yerine mevcut geliştirmeye yeni parametre eklemeyi tercih ediyor.Sonra proje birden kırmızıya dönmeye başlıyor. Neler oluyor, yazılımcı şaşkın. Projeye şöyle bir göz atıyor. Mevcut constructor eklenen parametreden dolayı kullanıldığı yerler hata vermiş. Neyse çok büyük problem değil hemen gereksiz yerlerde false değer veririrz yeter diyor. Geliştirmesini tamamlıyor.
Bir süre sonra telefonlara edge internet özelliği geliyor. Yazılımcı hemen bunuda PhoneFİlter sınıfına ekliyor.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
public class PhoneFilter { //fields... private boolean hasEdge; //constructors... public PhoneFilter(String brand, String model, String year, boolean hasGprs, boolean hasCamera, boolean hasEdge) { this.brand = brand; this.model = model; this.year = year; this.hasGprs = hasGprs; this.hasCamera = hasCamera; this.hasEdge = hasEdge; } //getter //setter } |
Teknoloji durmak bilmiyor 4G teknolojisi ve telefonların işletim sistemleri yaygınlaşıyor. hasGprs ve hasEdge ise artık kullanılmayacağı ve operatingSystem parametresi ekleneceği söyleniyor. Yazılımcı kolları sıvayıp geliştirmesini tamamlıyor ve kodun son hali aşağıda ki gibi oluyor.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
public class PhoneFilter { private String brand; private String model; private String year; private boolean hasGprs; private boolean hasCamera; private boolean hasEdge; private boolean hasLte; private String operatingSystem; public PhoneFilter(String brand) { this.brand = brand; } public PhoneFilter(String brand, String model) { this.brand = brand; this.model = model; } public PhoneFilter(String brand, String model, String year) { this.brand = brand; this.model = model; this.year = year; } public PhoneFilter(String brand, String model, String year, boolean hasGprs, boolean hasCamera, boolean hasEdge) { this.brand = brand; this.model = model; this.year = year; this.hasGprs = hasGprs; this.hasCamera = hasCamera; this.hasEdge = hasEdge; } public PhoneFilter(String brand, String model, String year, boolean hasCamera, boolean hasLte, String operatingSystem) { this(brand, model, year, false, hasCamera, false); this.hasLte = hasLte; this.operatingSystem= operatingSystem; } //getter //setter } |
Bu geliştirmede yazılımcı eski constructor kaldırmak yerine yeni construcor geliştirip, eski parametrelerin değerlerini mevcut constructor çağırarak veriyor. hasGprs ve hasEdge arıtk kullanılmayacağı için hard-coded olarak false veriyor. Koda baktığıınzda kaldırılmayan parametreler, sıralaması benzer yapıcı metotlar almış başını gidiyor.
Aradan zaman geçiyor ve ekibe yeni yazılımcı kaıtlıyor. Yazılımcı senin gibi bu yazıyı okurken, kendisine yeni bir geliştirme olacağı ve bu işi onun yapması isteniyor. Yazılımcı mutlu ve enerjik başladığı bir günde işi çok güzel anlıyor. Artık 4.5G teknolojisi içinde filtreleme yapılacak. Bunun içinde hasVolte parametresi eklensin isteniyor.
Yazılımcı bir sınıfı inceliyor bir çağırıldığı yeri inceliyor. Sınıfın çağırıldığı kodları bir araya getirip aşağıda ki gibi inceliyor.
1 2 3 4 5 6 7 8 9 10 11 |
public static void main(String[] args ) { PhoneFilter filter1 = new PhoneFilter("APPLE", "iPhone XS"); PhoneFilter filter2 = new PhoneFilter("APPLE", "iPhone XS", "2020"); PhoneFilter filter3 = new PhoneFilter("APPLE", "iPhone XS", "2020", true, false, false); PhoneFilter filter5 = new PhoneFilter("APPLE", "iPhone XS", "2020", false, true, true); PhoneFilter filter6 = new PhoneFilter("APPLE", "iPhone XS", "2020", true, true, "IOS"); } |
İlk üç parametreden sonrasında kafası allak bullak olmaya başlıyor. Sizinde kafanız karışmadı mı? Neyse ki yazılımcıda sizin gibi şanslı ve bu yazıyı okuduğu için refactoring yapmaya karar veriyor ve builder design pattern uygulamaya karar veriyor.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 |
public class PhoneFilter { private String brand; private String model; private String year; private boolean hasGprs; private boolean hasCamera; private boolean hasEdge; private boolean hasLte; private String operatingSystem; private PhoneFilter(Builder builder) { this.brand = builder.brand; this.model = builder.model; this.year = builder.year; this.hasGprs = builder.hasGprs; this.hasCamera = builder.hasCamera; this.hasEdge = builder.hasEdge; this.hasLte = builder.hasLte; this.operatingSystem = builder.operatingSystem; } public static class Builder { private String brand; private String model; private String year; private boolean hasGprs; private boolean hasCamera; private boolean hasEdge; private boolean hasLte; private String operatingSystem; public Builder(String brand, String model) { this.brand = brand; this.model = model; } public PhoneFilter build() { return new PhoneFilter(this); } public Builder setYear(String year) { this.year = year; return this; } public Builder setHasGprs(boolean hasGprs) { this.hasGprs = hasGprs; return this; } public Builder setHasCamera(boolean hasCamera) { this.hasCamera = hasCamera; return this; } public Builder setHasEdge(boolean hasEdge) { this.hasEdge = hasEdge; return this; } public Builder setHasLte(boolean hasLte) { this.hasLte = hasLte; return this; } public Builder setOperatingSystem(String operatingSystem) { this.operatingSystem = operatingSystem; return this; } } // only getters } |
İlk olarak Builder sınıfını konuşalım. PhoneFilter sınıfında yer alan aynı nesneleri ekledik. Hangi nesneye değer verildiğini görmemizi sağlayacak set metotlarını oluşturduk. Ayrıca geri dönüş değerinde ise yine çalıştığımız sınıfın o an ki nesnesini this ile verdik. Yapıcı metodunu oluşturduk. Yapıcı metotda zorunlu parametrelerimizi belirledik. build metodunda ise Builder sınıfının nesnesi üzerinden PhoneFilter metodunun oluşmasını sağladık. PhoneFilter nesnesinin oluşabilmesi içinise PhoneFilter sınıfında Builder sınıfını parametre olarak alan ve değerlerini ilgili nesnelere veren yapıcı metodunu oluşturduk. Artık sınıfımız hazır. Birde test edelim bakalımnasıl görünecek.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
public static void main(String[] args) { // PhoneFilter filter1 = new PhoneFilter("APPLE", "XS"); PhoneFilter filter1 = new PhoneFilter.Builder("APPLE", "IPHONE XS").build(); // PhoneFilter filter2 = new PhoneFilter("APPLE", "XS", "2020"); PhoneFilter filter2 = new PhoneFilter.Builder("APPLE", "IPHONE XS") .setYear("2020") .build(); //PhoneFilter filter3 = new PhoneFilter("APPLE", "XS", "2020", true, false, false); PhoneFilter filter3 = new PhoneFilter.Builder("APPLE", "IPHONE XS") .setYear("2020") .setHasGprs(true) .build(); //PhoneFilter filter5 = new PhoneFilter("APPLE", "XS", "2020", false, true, true); PhoneFilter filter5 = new PhoneFilter.Builder("APPLE", "IPHONE XS") .setYear("2020") .setHasEdge(true) .setHasCamera(true) .build(); //PhoneFilter filter6 = new PhoneFilter("APPLE", "XS", "2020", true, true, "IOS"); PhoneFilter filter6 = new PhoneFilter.Builder("APPLE", "IPHONE XS") .setYear("2020") .setHasCamera(true) .setHasLte(true) .setOperatingSystem("IOS") .build(); } |
Eski PhoneFilter nesnelerimizi yoruma aldıktan sonra her nesneyi Builder statik sınıfı aracılığı ile değerlerini verdik. Görüldüğü üzere iki parametrenin zorunlu olduğu geri kalanlarının ise sadece ihtiyaç duyulanların değerlerinin verildiği bir geliştirme yaptık. Build metodumuzu çağırmamızla beraber artık okunabilir düzeyde nesne oluşturan kodumuz tamamlandı.