Java Günlüğüm
Yazılım, Java, BT, azıcık felsefe, biraz mizah...
  • Udemy Eğitimleri
  • Temiz Kod
  • Tasarım Kalıpları
  • Hakkımda
  • Arşiv
RSS
15 Eylül 2016

Yaratımsal Kalıplar (Creational Patterns)

Akin Java, Tasarım Kalıpları, Yazılım Modelleme creational, Desen, design pattern, Şablon, tasarım kalıbı

Yaratımsal kalıpları (creational patterns) ele aldık ama bu konu hala bir yazıyı hakediyor. Çünkü yaratımsal kalıpların arasındaki ilişkileri ve hepsinin bir arada nesne yaratma problemini nasıl çözdüklerini görmek, nesne yaratma problemini ve çözümlerini bir arada görmek açısından çok faydalı olacaktır.

GoF’da 5 tane yaratımsal kalıp vardır. Bunlardan Singleton, diğer dördünden farklı ele alınabilecek yapıdadır. Çünkü Singleton, “nesne yaratma” hakkında bir kalıp değildir. Yani Singleton, nesnenin ya da nesnelerin nasıl yaratılacağıyla ilgili değildir, o çok daha özel bir problemi çözer: Bir sınıftan sadece ve sadece bir tane nesne nasıl yaratılır? Dolayısıyla Singleton‘u kenara koyarsak, geriye kalan dört kalıbı ve aralarındaki ilişkileri ele alabiliriz.

Singleton‘u çıkardığımızda geriye kalan dört kalıptan ikisi, Factory Method ve Abstract Factory, diğer ikisinden yani Builder ve Prototype‘tan görevleri itibariyle ayrılırlar. Dolayısıyla, geriye kalan dört kalıp, iki ayrı grupta ele alınabilir. Aralarındaki fark, ilk grubun yani Factory Method ve Abstract Factory kalıplarının nesne yaratmayı soyutlamalarına rağmen diğer iki kalıbın, yani Builder ve Prototype‘ın nesne yaratmayı soyutlamak yerine nesne yaratma mekanizmasını gerçekleştiriyor olmalarıdır. Bir başka deyişle, Factory Method ve Abstract Factory kalıplarının esas hedefi, nesne yaratma kodunun ortalıkta gezinmesini yani nesne yaratan kodların tekrarını önlemek ve bu kodları tekrar kullanılabilecek şekilde soyutlamaktır. Dolayısıyla Factory Method ve Abstract Factory kalıplarındaki metotlarda nesnelerin nasıl yaratılacakları, bu kalıpların çözdüğü bir şey değildir. Factory Method ve Abstract Factory kalıpları, “nesneleri nerede yaratalım?” sorusunun cevabıdırlar. Bu sorunun cevabını Factory Method ya da Abstract Factory olarak verdikten sonra soru değişir. Nesnenin nerede yaratılacağı belli olduğuna göre soru artık  “nesneyi nasıl yaratalım?”dır. Builder ve Prototype kalıpları ise GoF’un bu soruya verdikleri cevaplardır.

Malum,”nesneyi nasıl yaratalım?” sorusu klasik olarak iki cevaba sahiptir: ya kurucu çağrısı ile bütün gerekli veriyi parametre olarak geçerek nesne yaratma ya da önce varsayılan kurucu çağrısı ile en temel durumda nesne yaratma ve sonra da set metotları ile nesneyi istenilen duruma getirme. Bu ikinci durum “JavaBean modeli” olarak bilinir. Buna bir de Joshua Bloch’ın Effective Java kitabında tavsiye ettiği statik factory metodu yaklaşımı eklenirse, alternatifler 3’e çıkar.

Bu iki sorulu ayrım ve cevapları aşağıdaki şekilde özetlenmiştir:

Object Creation

Şimdi bu ayrımı aşağıdaki örnekte ele alalım.

Örneğin elimizde bir tane Car sınıfı var ve uygulamada bu sınıfın değişik nesnelerini oluşturmamız gerekiyorsa, nesne oluşturan kodları bir Factory Method’un içine koyarız. Yok elimizde Car ile beraber Engine, Brake, Wheel, Door vb. sınıflar da varsa ve bu sınıfların nesnelerini biz bir arada oluşturuyorsak, bu durumda her birisi için gerekli olan Factory Method‘unu bir sınıfın içine koyar ve Abstract Factory‘i elde ederiz. (Açıkçası  Factory Method, AbstractFactory‘nin tek nesne için olan halidir. Bu yüzden ikisinin Factory adıyla tek bir kalıp olarak ele alınması daha iyi bir durum olur. Fakat GoF, bir hata yapıp ikisini ayırmış ve nihayetinde karmaşaya yol açmıştır. Bu konuda buraya bakılabilir.) Hatta eğer farklı Car nesneleri için Brake, Wheel, Door vb. sınıfların farklı durumda nesneler oluşturuluyorsa, bu halde her nesne ailesi için ayrı bir Abstract Factory sınıfı oluştururuz.

Buraya kadar sistemde var olan Car, Brake, Wheel, Door vb. sınıfların nesnelerinin nerede oluşturulacağı ile ilgili gerekli yapıyı kurguladık ama ister Abstract Factory olsun ister Factory Method olsun, nihayetinde nesne oluşturan üretici yani factory metotlarının içinde nesneleri gerçekte nasıl üreteceğimizi konuşmadık. İşte şimdi de bu kısma geldiğimizde elimizde hangi alternatiflerin olduğunu ele almalıyız.

Factory Method ve Abstract Factory kalıplarında nesneleri üretmek için temelde beş yöntemimiz vardır. İlk ikisi ve handikapları şöyledir:

  • Kurucu (constructor) çağrısı: En iyi bilineni parametreli kurucu çağrısı ile nesne oluşturmak. Kurucular, isimlerinin aynı olmasınan dolayı karıştırılılar ve çoğu zaman hataya yol açarlar. Ayrıca artan parametre sayısı kurucu çağrılarını gittikçe zorlaştırır.
  • JavaBean yaklaşımı: Varsayılan kurucu ile nesneyi oluşturup set metotlarıyla nesneyi istenilen duruma getirmek. Bu yaklaşım da kurucu çağrısı gibi problemlidir, hataya yol açabilir.

Yukarıdaki iki alternatif de basit nesnelerin oluşturulması için kullanılmalıdır. Basit nesnelerden kasıt ise kurucusuna geçilen parametre sayısı ya da JavaBean yaklaşımında çağrılacak set metodu sayısı 3-4’ü geçmeyendir. Hele JavaBean yaklaşımında set edilecek durum parametreleri arasında bağımlılık varsa ve bu set metotlarının çağrı sırasını belirliyorsa bu durumda hataya çok elverişli bir durum var demektir, kaçınılmalıdır. Bu halde aşağıdaki yollar tercih edilmelidir.

Peki, geriye kalan üç alternatif nedir:

  • Joshua Bloch’ın statik factory metotları: Joshua Bloch, Effective Java kitabında, nesne oluşturmak için, sınıfta tanımlanan karmaşık yani çok argüman alan kurucular yerine, anlamlı isimlere sahip statik factory (nesne üretici) metotları önermektedir. Buna göre kurucular private yapılır ve var olan her kurucu için,aynı sınıfta, statik, nesne yaratan yani factory metotlar oluşturulur öyle ki bu metotların isimleri, yarattıkları nesnenin durumunu göz önüne alacak şekilde verilir. “createAuthenticatedUnvalidatedCorporateUser” gibi isimlerle, hem kullanıcının durumuyla ilgili geçilecek ikisi muhtemelan boolean tipinde, üç ya da daha fazla  argümandan kurtulunmuş olur hem de kurucuların isimlerinin aynı olmasının getirdiği karıştırma problemi oradan kalkar. Bu konuda ayrıntılı bilgi için Bloch’ın Effective Java kitabına bakılmalıdır.
  • Builder (İnşacı): Eğer nesne karmaşık ve bu karmaşıklık nesnenin oluşturulma şeklini de belirliyorsa, nesnenin oluşturulması bir süreç gerektiriyor demektir. Bu durumda nesnenin yaratılmasından değil de oluşturulmasından, inşa edilmesinden bahsedilir. Bu şekilde, nesneyi bir süreç dahilinde oluşturmayı öneren kalıp, Builder ya da İnşacı kalıbıdır. Dolayısıyla karmaşık nesneler, bir süreç dahilinde bu kalıp ile oluşturulmalıdırlar.
  • Prototype (Örnek Nesne): Prototype da Builder gibi, oluşturulması zor dolayısıyla da yüksek karmaşıklıktaki nesnelerin yaratılması içindir ama ufak bir farkla. Prototype, yeni nesneyi, var olan bir nesneden oluşturur. Yani önce var olan örnek (prototype) nesneyi kopyalayar (clone) sonra istenilen duruma getirir. Bu yüzden, Prototype kalıbının odağı, durumdan (state) kaynaklanan karmaşıklıktır. Bu da şu anlama gelir: Eğer nesnenizin karmaşıklığı, nesnenin sahip olduğu durumlardan kaynaklanıyorsa, yani nesneniz iş için belirlenmiş bir kaç durumdan birinde olması gerekiyor ve developerların hepsinin bu durumları detaylarıyla bilmesi zor ise, o nesneyi bu durumlardan birinde oluşturmak zor ve hataya elverişli bir durum var demektir. Bu durumda nesneyi sıfırdan oluşturmak yerine, arzu ettiğiniz durumda olan bir nesneyi kopyalayarak elde etmek daha kolay olur. Bu da Prototype kalıbıdır.

Yukarıdaki alternatiflerle ilgili şunlar söylenebilir:

  • Nesneleri daima bu işe hasredilmiş üretici (factory) sınıflarında oluşturun. Oluşturulacak nesneler birbirleriyle ilgili değillerse, her birisi için bir Factory Method kullanın, yok nesneler bir arada kullanılıyor ve muhtemelen de beraber oluşturuluyorsa, bu durumda nesne ailelerinden bahsediliyor demektir ve bunun için Abstract Factory‘i kullanın.
  • Factory Method, AbstractFactory‘nin tek nesne için olan halidir. Dolayısıyla Abstract Factory, Factory Method‘u kullanır. (Aslen her iki kalıp da Factory isimli genel kalıbın altında ele alınmalıdır.)
  • Factory Method (dolayısıyla da AbstractFactory), nesne oluşturmak için Builder ya da Prototype kalıplarını kullanabileceği gibi, nesneyi yaratmak için kurucu cağrısı, JavaBean yöntemini ya da Bloch’ın statik factory metotlarını da tercih edebilir.
  • Belki garip gelecek ama yukarıdakinin tersine, Builder da Factory Method‘u (dolayısıyla da AbstractFactory) kullanabilir çünkü nesneyi inşa sürecinde muhtemelen pek çok, esas nesnenin ihtiyacı olan diğer, muhtemelen farklı türlerden nesneler de oluşturulacaktır. Benzer şekilde esas nesnenin inşa sürecinde gerekli diğer nesnelerin yaratılması için, kurucu cağrısı, JavaBean modeli ya da Bloch’ın statik factory metotları da kullanılabilir.

Aşağıdaki şekil, yukarıda anlatılan seçenekleri ve kalıplar arasındaki ilişkileri özetlemektedir.

Creational Patterns

 

Bu yazıda nesne yaratmayla ilgili hem kalıpları hem de klasik diğer yolları ve Bloch’un önerisini ve aralarındaki ilişkileri ele aldık. Bu şekilde konu ile ilgili büyük resmi görmeye ve daha analitik bir anlayışa sahip olmaya çalıştık.

 

Toplam görüntülenme sayısı: 1742

10 Bunu beğendim 🙂
Tweet
Follow me
Tweet to @kaldiroglu
08 Şubat 2016

Kod Analizi: Kurucu Kullanımı

Akin Java factory, Kalıp, kod analizi, pattern, üretici metot

Danışmanlık verdiğim yerlerde her zaman farklı yaklaşımlarla yazılmış kodlar görüyorum. Bu tür kodların ufkumu açtığında şüphe yok. Ama açıkçası her ufuk açmanın iyiye ve güzele doğru olduğunu söylemek zor.

Aşağıdaki kod da bu şekilde karşılaştığım kodlardan. Tabi ki ciddi miktarda kamufle edilmiş isimler içeriyor. Ama koddaki yaklaşım, arka taraftaki düşünce vs. tamamen korunmuş durumda. Kod şöyle:

public JTUser execute(RegistrationForm form, 
          HttpServletRequest request, Locale locale)    
          throws JTException {

	String uid = Utils.generateUUID();
	String salt = Utils.generateSalt(32);
	String accountId = Utils.generateUUID();

	JTUser user = new JTUser(form.getEmail().trim(), null, true, true, 
                          true, true, AuthorityUtils.NO_AUTHORITIES, uid, 
                          form.getEmail().trim(), salt, 
                          form.getName().trim(), form.getSurname().trim(), null, accountId, true,
		          true, form.getWelcomeMessage().trim(), true, new Date(), null);

	user.setStatus(JTConstants.PENDING);

	String servletPath = request.getServletPath();
	if (executeInner(form, locale, user)) {
	    auditLogDao.insert(new AuditLog(user.getUid(), user.getUsername(), Utils.getRemoteIp(), Utils.getSid(),
		               AuditType.REGISTRATION, AuditSubtype.PENDING, ResponseCode.SUCCESS, 
                               new Date(), null));
	    authenticateFastRegisterUser(form, request);
	}

    return user;
}

Bu metodu analiz etmek istersek, hem nesne-merkezli programlama hem de temiz kod gibi noktalardan inceleyebiliriz. Önce şekilden başlayıp sonra kodu yazanların sahip oldukları, en azından koda yansıttıkları anlayışa doğru gidebiliriz. Bu yüzden önce isimlerden başlayalım.

Mekanı kullanma

Yukarıdaki kodda düzgün olan nedir diye sorsam, olsa olsa “sadece paragraflar” denebilir çünkü isimlendirme bile problemli. Ama açıkçası bir kodun paragraflandırılmış olması, yani bir geliştirme aracının iki-üç tuşa basımlık bir işini meziyet olarak ortaya koymak mümkün değil. Öte taraftan JTUser kurucu ve insert metot çağrılarının, kodda hüküm süren yaklaşımdan dolayı mekanı zorunlu olarak iyi kullanamadıkları da bir gerçek.

İsimlendirme

Çoğu zaman kodu yazanların kafalarındaki karmaşıklığı verdikleri isimlerden çıkarabilirsiniz. Bu kod parçasında da execute ve executeInner metotları, bu metotları yazanların kafalarındaki karmaşıklığa bir örnektir. Biraz empati yapıp, execute metodunun Command tasarım kalıbından esinlendiği düşünülebilir. Bu durumda daha geniş bağlama bakıp hakikatten bu kalıbın kullanıp kullanılmadığına karar verilebilir. Diyelim ki Command kalıbı kullanıldı ve bu metodun bulunduğu sınıf, kalıptaki arayüzü gerçekleştiriyor ve bu yüzden metodun ismi, Command kalıbının geleneğine bağlı kalınarak execute olarak verilmiş olsun. Peki executeInner‘ı ne yapacağız? executeInner metodunun içinden çağrılan metotların isimlerini tahmin edebilir miyiz? executeIpInner, executeDahaDaInner ya da executeInner2 diye gidebilir 🙂  Bu kadar kötü ve ezbere bir isimlendirme olursa, daha fazla düşünce ve tecrübe gerektiren, daha zorlu algoritmik ve modelleme problemlerinde nasıl bir yaklaşım olabilir, tahmin etmek zor değil.

Bir diğer isimlendirme problemi de authenticateFastRegisterUser metodu için söz konusu. Bu metot hali hazırda sisteme kayıtlı olan kullanıcının hızlı olarak sisteme girmesini sağlıyor gibi duruyor. Bu durumda metot isminin authenticateFastRegisteredUser olması gerekirdi.

Yaklaşım

Bu kod parçasında temelde JTUser oluşturulup executeInner‘a geçiliyor ve dönen değer true olduğunda göre  audit bilgisi AuditLog nesnesi olarak oluşturulup, veri tabanına gönderiliyor ve user sisteme dahil ediliyor. Bu işler olurken kodda göze batan iki yer var, iki nesnenin oluşturulması. Bu iki nesne için tabi olarak ikş kurucu çağrısı yapılıyor ve bu çağrılar o kadar kötü kurgulanmış ki insan hayret ediyor. Şu çağrıya bakın örneğin:

	JTUser user = new JTUser(form.getEmail().trim(), null, true, true, 
                          true, true, AuthorityUtils.NO_AUTHORITIES, uid, 
                          form.getEmail().trim(), salt, 
                          form.getName().trim(), form.getSurname().trim(), null, accountId, true,
		          true, form.getWelcomeMessage().trim(), true, new Date(), null);

Bir kurucuya neden nesneyi doğrudan geçmek yerine parçalarını ayrı ayrı geçersiniz ki? Bu kurucuya form nesnesinin email, name, surname ve welcomeMessage alanları ayrı ayrı geçiliyor. Aslen sadece form nesnesinin geçilmesi gerekirdi. Bu durum nesne-merkezli programlamanın pek özümsenmediğini açıkça gösteriyor.

Öte taraftan, bir metoda neden new Date() ile o anki zamanı geçersiniz? İki şeyden dolayı, ya geçilen yerde o anki zamana ulaşamıyorsunuzdur,  yani kurucuda new Date() yazamıyorsunuzdur :), ya da o kadar ince hesaplarla çalışıyorsunuzdur ki, önemli olan execute metodundaki zamandır, bir nano saniye sonra JTUser kurucusundaki an işinize yaramıyordur 🙂 Açık ki burada bu iki durum da söz konusu değildir.

Başka ne gibi problemler var bu kurucu çağrısında? null geçilmesi problemdir. Üç parametre null olarak geçiliyor ve muhtemelen başka yerlerdeki aynı kurucu çağrısında null geçilmiyordur. Bu durumda da kurucuda devamlı null kontrolü yapılmalıdır. Aslen hep null geçiliyorsa bu daha büyük bir problemdir çünkü bu o nesnenin zaten bu kurucuya hiç geçilmemesi gerektiğini gösterir. Yok ilk durum geçerliyse yani bazen null geçiliyor bazen de değer geçiliyorsa bu zaten sıkıntılı bir yapı ortaya koyar çünkü null geçmek ya da null döndürmek hataya sebebiyet verir.

Kurucuya geçilen diğer üç parametre yani uid, salt, accountId, de geçilmemelidir çünkü bu üç değer zaten Utils sınıfından statik metotlarla alınabilir. Ne farkeder ki, ha orada ha burada demeyin, en az parametre geçecek şekilde kod yazmamız gerekli. Aksi taktirde bağımlılıkları gereksiz yere arttırmış oluyoruz.

Geriye bu kurucuya geçilen yedi tane true parametresi kalıyor. Bu parametreler belli ki JTUser nesnesinin belli bir durumda (state) oluşmasını sağlıyorlar. Ama bir nesnenin durumunda yedi tane boolean değişken yer alması biraz garip değil mi? Bu yedi değişken toplamda 2^7 = 128 farklı durumu temsil eder. Bu durumda JTUser nesnesinin en az 128 farklı hali var demektir. Örneğin yeni kayıt olmuş ama henüz teyit edilmemiş kullanıcı, yeni kayıt olmuş ve teyit edilmiş kullanıcı, vs. diye gidebilir. Problem şu: Her ne kadar biz metotları, onlara az parametre geçilecek şekilde tasarlayalım desek de kurucularda böyle yapmak imkansızdır. Yani kurucular nihayetinde oluşturdukları nesnelerin durumlarından sorumlu oldukları için genelde çok sayıda parametre alırlar. Bu türden kuruculara, parametrelerden dolayı uzun olduklarını ifade etmek için “teleskopik” denir. Biz yukarıda saydığımız sıkıntıları giderecek şekilde kodu tekrar yazsak bile kurucular hala çok parametre alır halde kalırlar. Örneğin JTUser kurucusunun 20 olan parametre sayısını 8’e indirmiş oluruz, ve bu hale gelir:

	JTUser user = new JTUser(form, true, true, true, true, true, true, true);

Garip değil mi? Garip olan şey bu kurucu metodun uzunluğu değil, çağrılmasındaki zorluk. Yedi tane arka arkaya gelen boolean parametrenin hangisinin ne olduğunu ve ne işe yaradığını bilmeniz gerekli. Muhtemelen bu kurucu metot şöyle tanımlanmıştır:

...

public JTUser(Form form, boolean verified, boolean authorized, boolean authenticated, ...){
   ...
}

...

Çok fazla sayıda parametre içeren metot yazmanın kaçınılmaz sonuçlarından birisidir bu durum. Normal metotlarda, metotları bölüp parçalayarak, sadece br işe odaklar hale getirip  bu durumdan kaçınabiliriz. Fakat metotların ismi olmasına karşın kurucuların ayırt edici isimleri yoktur. Ve özellikle de karmaşık nesnelerde hem pek çok kurucu vardır ama hangisinin, hem de kurucuların kendileri uzundur. Dolayısıyla hangi kurucunun çağrılacağı ve hangi parametrelerin geçileceği, zaman alan bir kodlama gerektirir ve hataya çok açıktır. Öyleyse kurucularda bu durumdan nasıl kaçınılabilinir?

Aslında bunun bir kaç yöntemi var. Örneğin yaratımsal (creational) kalıpları kullanmak. Ama nasıl bir çözüm önerilirse önerilsin yapılması gereken en temel şey, geçilen boolean parametreleri ortadan kaldırıp ya da en azından azaltıp, parametrenin rolünü metodun isminde ifade edecek şekilde kurgulanmış isimlere sahip, nesne oluşturan, gerekirse uzun isimli ama anlamlı metotlar yazmak. Eğer bu metotları ayrı bir sınıfta yazarsanız üretici metot (factory method) kalıbına varırsınız. Bu durumda bu sınıf şöyle olacaktır:

public class JTUserFactory{
	
	public JTUser produceVerifiedJTUser(...){ ... }

	public JTUser produceVerifiedJTUserUsingForm(Form form, ...){ ... }

	public JTUser produceVerifiedAndAuthorizedJTUserUsingForm(Form form, ...){ ... }

	...
}

(Üretici metot (factory method ) kalıbı için buraya bakabilirsiniz.)

Benzer ama bir başka çözüm, J. Bloch “Effective Java 2nd Ed.” isimli kitabının ilk maddesinde ifade edilmiştir. Burada tekli edilen şey, sınıfta kurucu kullanmak yerine statik factory metotlarının kullanılmasıdır. Yani yukarıdaki metotların statik olarak JTUser sınıfında olduğu durumdur. Ayrıca gerekirse bu metotları kullanmak için JTUser‘ın tüm kurucuları private yapılabilir.

Böyle bir yol izlendiğinde yukarıdaki kodumuz şu hale gelecektir:

public void authenticateUser(RegistrationForm registrationForm, 
                             HttpServletRequest request, 
                             Locale locale) throws JVTException {

   JVTUser user = JVTUser.createUnauthenticatedUser(registrationForm);

   if(authenticateUserUsingForm(registrationForm, locale, user)){
           
      authenticateFastRegisterUser(registrationForm, request);    
      
      mailSender.sendMail(user, locale);

      auditLogDao.insert(
          AuditLog.createAuditLogForUserAuthentication(user));
   }
}

Bu kodda, JTUser ve AuditLog sınıfları statik üretici metotlara sahip olacak şekilde kurgulandıklarından, nesnelerini oluşturmak createUnauthenticatedUser ya da createAuditLogForUserAuthentication gibi metotlarla çok daha rahat hale gelmiştir.

Kodun ilk halinin en berbat tarafı, sadece kötü yazılmış olması değildir. Çoğu zaman bu kodun sadece böyle yazılmış olmasına odaklanır esas problemi gözden kaçırırız. İlk haldeki gibi yazılmış karmaşık kodlar, copy-paste ile her yere dağılır. Her JTUser nesnesine ihtiyacı olan bu ve buna benzer kurucu çağrılarını, çağrıyı yapmak zor olduğundan, haklı olarak copy-paste ile alıp kullanma eğiliminde olur. Bu da bakımı çok zor bir kod yapısına götürür. Eğer bu çağrıyı yapmak için copy-paste’e başvuracak kişi, biraz akıllıca davranıp, yukarıda bahsedilen yollardan birisiyle, cut-paste kullanarak bu kodu iyileştirse (refactoring) idi çok güzel bir şey yapmış olurdu ve bu kod yapısı da iyileşerek büyürdü.

Burada kurucular üzerinden bir kod analizi yaparak nasıl daha temiz, nesne-merkezli ve bakımlanabilecek kod yazılabileceğini ele aldık. Ve yine temiz kod için iyileştirmeye (refactoring) ihtiyacımız oldu.

Rahat bakımlanabilir kodlar yazmak dileğiyle 🙂

Toplam görüntülenme sayısı: 1083

24 Bunu beğendim 🙂
Tweet
Follow me
Tweet to @kaldiroglu
28 Ocak 2016

Tasarım Kalıpları – II: Neden Öğrenelim?

Akin Java, Tasarım Kalıpları, Yazılım Mühendisliği design pattern, tasarım şablonu

Bu yazıda tasarım kalıplarını neden öğrenmemiz gerektiğini ele alalım.

“Tasarım Kalıpları” tamlaması, kullanıldığında bir “üst perde” havası oluşturan bir kavrama karşılık geliyor. Sanki bu tamlamayı kullanan kişi birden “erenler”den oluveriyor. Kullanıldığında konuya ve özellikle de kullanana “ileri derecede karmaşıklık” ya da “sofistikasyon” atfediyor. Halbuki durum öyle değil, ama belli ki bu algı bizim cehaletimizden kaynaklanıyor.

Bir de bu konuda öğrenilmiş çaresizlik yaşıyor gibiyiz. Ben üç-beş defa bu konuyu öğrenmeye niyetlenmiş ve girişmiş ama her seferinde belli bir yerde bırakmak zorunda kalmış insanlarla tanıştım. Ülkemizdeki “İngilizce”nin durumu gibi 🙁 Sanki bilmek zorundaymışız, ciddi bir miktar zaman harcamışlığımız da var ama malesef henüz tam olarak bilmiyoruz gibi bir durumdayız. Her eğitimde ya da danışmanlıkta müşteri tarafında olan yazılımcı arkadaşlar bazen bir miktar uğraştıklarını göstermek, bazen kötü durumda olan kodlarının bu durumunu açıklamak ya da doğrudan benimle sohbet etmek vs. gibi sebep ve niyetlerle tasarım kalıpları tamlamasını ya da bazı tasarım kalıplarını telaffuz ederler.

Tasarım kalıpları etrafındaki oluşan bu buğulu havayı dağıtmak, başarılı bir öğrenim süreci için elzem görünüyor.

Tasarım kalıplarını kullanmadan program yazmaya başladığınızda ilk yapacağınız hata, program olarak ifade etmeye çalıştığınız iş süreci ya da problemindeki sorumlulukları roller olarak ayırt edememenizdir. Yani “bu problemin çözümünde hangi sorumluluklar var?” ve “bu sorumluluklar farklı nesnelere nasıl dağıtılmalı?” sorularının sorulması ve cevaplanması aslen bir tasarım faaliyetidir. Hatta tasarım daha doğrusu fonksiyonel tasarım faaliyeti temelde bu soruları açıklığa kavuşturmak, alternatif cevaplar verip bunları kıyaslayarak sonuca ulaşmadan ibarettir bile denebilir. Tasarım yapma niyetiniz yoksa ki bunu biz “zamanımız yok” diye ifade ederiz, zaten bu yazının geri kalanı anlamsız olur 🙂 Yok, doğru düzgün bir yapı ortaya çıkarmak için tasarıma niyetimiz varsa tasarıma vakit ayırırız ki yukarıdaki soruları sorarız ve tasarım kalıplarının yardımıyla vereceğimiz cevaplarla nesne modellerini oluştururuz.

Tasarım yapılmazsa dolayısıyla da tasarım kalıpları kullanılmazsa, istenildiği kadar en yeni teknolojiler kullanılsın ya da nesne-merkezli bir dille program yazılsın, sağlıklı bir nesne yapısı kurgulama ihtimali sıfıra yakındır. Çünkü, yukarıda bahsettiğim, sorumlulukları bulma ve nesnelere dağıtma işinin sağlıklı yapılmaması söz konusudur. Bu durumda da gidilecek yön, nesne-merkezli programlama değil, olsa olsa prosedürel programlama hatta daha da kötüsü, karman-çorman kod yığınıdır.

Örneğin, normalde tasarım yaparak ve tasarım şablonlarını kullanarak nesnelere ve üzerinde muhtemelen override edilmiş metotlara bölünecek bir davranışın, tek bir metot altında pek çok “if” kullanarak kodlandığı sıklıkla görülür. Bu durum ise olsa olsa “functional decomposition”dır ve başınızı gittikçe belaya sokan bir yaklaşımdır. Dolayısıyla tasarım kalıpları çoğu defa bize, normalde nesne-merkezli programlama dili kullanmamıza rağmen prosedürel anlayışla yazılım geliştirmeye düşmekten kaçınmamızı sağlar. Tam da bu yüzden pek çok refactoring yani kod iyileştirmesi tekniği, var olan karmaşık ve devasa hale gelmiş nesne ve metotları, farklı kalıpları kullanarak, ufak-tefek nesne ve metotlara dönüştürür. Tasarım kalıplarının bunu yaparken elinde tuttuğu temel teknik, yukarıda bahsettiğim iki sorudur: “bu problemin çözümünde hangi sorumluluklar var?” ve “bu sorumluluklar farklı nesnelere nasıl dağıtılmalı?” Zaten bu sorular sorulmadığından devasa sınıflara ve metotlara ulaşılmıştır.

Ben danışmanlıklarımda, özellikle kod kalitesini arttırıcı işlerimde bu durumlarla çok sık karşılaşıyorum. Tek yapacağım şey konuyu anladıktan sonra bu iki soruyu sormak. Tasarım kalıpları, bu iki sorunun cevabını ciddi oranda veriyorlar. Çünkü tasarım kalıpları hem bir yöntemdir hem de bir sorumluluk kataloğudur. Tasarım kalıpları yöntemdir çünkü bu iki soruyu sormanızı ve nasıl cevap vereceğinizi size öğretir. Tasarım kalıpları aynı zamanda bir sorumluluk kataloğudur, çünkü en basitinden en karmaşığına kadar, farklı sektördeki yazılımlarda bulunan en yaygın sorumlulukları ortaya koyar. Dahası tasarım kalıpları, bu sorumlulukları nasıl bulacağınızı ve bunları iç tutarlılığı yüksek (highly-cohesive) ve dış bağımlılığı düşük (lowly-coupled) nesne modellerine nasıl çevireceğimizi de bize gösterir.

Eğer tasarım faaliyeti, yazılım geliştirme sürecinizin bir parçası ise zaten yolunuz bir şekilde kalıplarla kesişiyor demektir. Yani sadece Gof’un 23 kalıbını kullanmakla kalmıyor, kendi iş alanınızda sıklıkla karşılaştığınız sorumluluk problemlerine özel kalıplar da geliştiriyorsunuz demektir.

Dolayısıyla tasarım kalıplarını şu amaçlarla öğrenmeliyiz:

  • Tekerleği yeniden keşfetmemek, var olan ispatlanmış çözümleri kullanmak: Tasarım kalıpları sıklıkla karşılaştığımız, yaygın sorumluluk bulma ve dağıtma problemlerini çözerek, tekrar kullanılabilen, faydası ispatlanmış modeller ortaya koyar.
  • Formal ve yaygın bir dil oluşturmak: Tasarım kalıpları, formal bir kalıp dili (pattern language) oluşturmaktadır. Bu kalıp dilinin en temel kavramı sorumluluktur. Dolayısıyla tasarım kalıpları zihnimize, “şu şey yapar, şunu alır şuna verir” gibisinden sokak ağzı bir konuşma yerine “kontrol nesnesi (controller), request ve response nesnelerini, ilgili üreticilerden (factory) alıp sunum (view) nesnesine geçer” şeklinde konuşmayı öğretir. Bu dilin yazılım geliştiriciler arasında yayılması, anlaşmayı kolaylaştıracak, ifade gücümüzü arttıracaktır.
  • Tasarıma uygun, yüksek soyutlama gücü kazandımak, detaylardan sıyrılıp, daha yüksek hedefler cinsinden düşünmek: Bu anlamda tasarım kalıpları daha iyi bir yazılım geliştirici hatta mühendisi olmanın en temel gerekliliklerindendir.

Zaman zaman karşılaşıyorum, “biz aslında bir miktar tasarım yapmıştık” şeklinde cümlelerle. Bırakın kalıbı sistemde hiç arayüz yok, hiç bir nesne hiyerarşisi yok, devasa sınıflar ve metotlar var, nesneler arası ilişkiler karmakarışık, kim neyi biliyor belli değil, peki tasarım nerede? Yukarıda anlattığım gibi tasarım yapılacaksa o iki sorudur üzerinde düşünülmesi ve çözülmesi gereken şey. Ve yolunuz bir şekilde tasarım kalıplarıyla kesişir.

Tasarım kalıplarını bilmiyorsanız, tasarım da yapamazsınız.

Bol tasarımlı günler dilerim 🙂

Toplam görüntülenme sayısı: 1807

30 Bunu beğendim 🙂
Tweet
Follow me
Tweet to @kaldiroglu
29 Aralık 2015

Staj – 2016

Akin Diğer

Sevgili JavaTürk takipçileri,

Yazılım Mühendisliği ve teknolojik olarak da Java alanlarında yaptığım çalışmalarda bana yardımcı olacak1 ya da 2 tane stajyer arkadaşa ihtiyacım var. Stajyer arkadaşların sorumlulukları şunları içerecektir:

  • YM ve Java başta olmak üzere değişik konularda AR-GE faaliyetlerinde bulunmak,
  • Yürüttüğüm danışmanlık ve eğitim faaliyetlerinde bir miktar kırtasiye de dahil olmak üzere yardımcı olmak.

Staj çalışmalarının bir kısmını kendi yerinizden kalkmadan yapabileceğiniz gibi ofisimizde de yapabilirsiniz.

Aday arkadaşların öncelikle yaptıkları işi çok sevmeleri, tutkulu olmaları şart. Temel konulardaki bilgileri de sağlam olmalı. Yani analitik ve matematiksel düşünme yetenekleri yanında yazılım ve Java dili ile ilgili temel bilgileri haiz olmalılar. Algoritmik düşünce yetkinliklerini kazanmış, nesne-merkezli programlama konusunda temel yapıları özümsemiş ve tercihen Java SE kullanan ve kendini daha da geliştirmek isteyen adayları tercih ederim. Bu anlamda şart olmamakla birlikte, daha yukarı sınıflardaki arkadaşlarla çalışmamız sanırım daha rahat olacaktır. İlgisini, tutkusunu ve bilgisini gösteren herkese kapım açık olmakla birlikte…

İlgilenlerin bana buradan CVlerini paylaşarak ulaşmaları yeterli olacaktır. Lütfen CVlerinizi göndermeden önce bir daha kontrol edin, CV’de yazana değer verdiğimi ve doğru olduğunu düşünmek dışında bir seçeneği göz önüne almadığımı da bilin. Dolayısıyla Cvnizi göndermeden önce şunlara son bir daha göz atın:

  • Mahrem bilgilerinizi CVnize yazmayın. Evli olup olmadığınız, değilseniz kız-erkek arkadaşınızın olup olmadığıyla ilgilenmiyorum 🙂
  • 3 senelik okul hayatına ve 0 günlük profesyonel çalışma tecrübesine rağmen “4 senelik tecrübe” yazmayın,
  • CVnizi teknoloji çorbasına çevirmeyin. “25 seneye sığacak tecrübeyi ben 2 senede elde ettim” demeye getirmeyin. Dolayısıyla seminerine gittiğiniz teknolojiyi “biliyorum” diye yazmayın. Arzu ederseniz bu blogdaki “bilmek” ile ilgili yazılarımı okuyun 🙂
  • CV yazmak sizin kendinizi tanımınızı sağlayabilir. Dolayısıyla “ben zaten XXX’i biliyorum” demek yerine “XXX’i daha iyi öğrenmek istiyorum” gibi cümle kurmaya CV yazarken başlayabilirsiniz. Ben de açıkçası bunu tercih ederim
  • MS Office ürünlerini teknoloji kısmında sıralamayın.
  • İngilizce, bu staj için önemli. “Okuma: 10, Dinleme: 10, Konuşma: 9” şeklinde gelen CV sahiplerinin “Hi, I am calling you from Selsoft regarding your intern application. How are you doing today sir/ma’m?” şeklinde başlayan bir telefon alabileceklerini göz önüne almaları gereklidir.

Bu ülkedeki bir üniversite öğrencisinin neyi ne kadar bilebileceğini fena bir şekilde farkındayım. Beni kandırmak yerine şaşırtmaya çalışın. Bildiklerinizle ilgileniyorum fikir sahibi olduklarınızla değil! Yapabildiklerinizle ilgileniyorum bildiklerinizle değil! Yaptıklarınızla ilgileniyorum yapabildiklerinizle değil!

Ayrıca bu kadar da soğuk bir adam değilim, geyik ve keyifli sohbeti çok severim 🙂

CVlerinizi bekliyorum.

Toplam görüntülenme sayısı: 1778

32 Bunu beğendim 🙂
Tweet
Follow me
Tweet to @kaldiroglu
«< 6 7 8 9 10 >»

Günlüğüme Hoşgeldiniz

Bu günlükte, Yazılım Mühendisliği, Bilgi Teknolojileri, Java, kişisel gelişim ve zaman zaman da diğer konulardaki düşüncelerimi sizlerle paylaşacağım. Umarım beğenir ve hoşça vakit geçirirsiniz.

Her türlü düşüncenizi, yorum olsun, beğeni ya da eleştiri olsun, bana iletmenizi rica ediyorum sizden. Ayrıca bana akin@javaturk.org adresinden ya da Twitter'dan ulaşabilirsiniz. Videolarıma da buradan ulaşabilirsiniz.

Teşekkür ederim.

Akın Kaldıroğlu

Rahat Okumak İçin

A Decrease font size. A Reset font size. A Increase font size.

Sosyal Medya

  • Twitter
  • Facebook
  • LinkedIn
  • Youtube

Son Twitlerim

→ Takip Etmek İçin

Abone Olun

Emalinizi girerek yazılardan haberdar olun.
Loading

Son Yazılarım

  • Udemy Eğitimlerim Üzerine
  • (başlıksız)
  • Clean Code / Temiz Kod Eğitimi Udemy’de
  • Java ile Nesne-Merkezli Programlamaya Giriş Eğitimi Udemy’de
  • Selsoft Video Eğitimleri
  • Spring ile Kurumsal Yazılım Geliştirme
  • Corona Günlerinde Design Patterns
  • Corona Günlerinde Java
  • JDK 10 ve “var” Özelliği
  • Onur Özcan
  • Analist ve İş Bilgisi
  • Farklı Dillerin Bakış Açısıyla Nesne-Merkezli Programlama
  • Java Nedir?
  • Bilgi Teknolojilerinde Yetenek Yönetimi – II: Tanımlar ve Eleştiriler – I
  • Alelade Hikayeler – II: Bir Başka Performans Problemi

Popüler Yazılar ve Sayfalar

  • Java’ya Nasıl Başlarım? Java’yı Nasıl Öğrenirim? – I
  • Nasıl Yazılımcı Olalım? – II: Hangi Bölümü Okuyalım?
  • Oracle’ın Java SE Sertifikaları: OCA, OCP ve OCM
  • Java Kurulumu ve İlk Programımız
  • İş Analisti İş Tanımı
  • Java Tutorial ya da Kendi Kendine Java Öğren
  • Nasıl Yazılımcı Olalım? – I: Üniversiteli mi Alaylı mı?
  • Tasarım Kalıpları
  • Java’ya Nasıl Başlarım? Java’yı Nasıl Öğrenirim?
  • UML Nedir?

Yazı Kategorileri

Yazı Takvimi

Haziran 2025
P S Ç P C C P
 1
2345678
9101112131415
16171819202122
23242526272829
30  
« May    

Yazı Arşivi

Blogroll

  • Binnur Kurt'un Günlüğü
  • Ender'in Java Blogu
  • Erdem Seherler
  • Kızımın Günlüğü
  • Kurumsal Java
  • Levent Karagöl
  • Levent'in Java Blogu
  • Mert Can Akkan’s java tips,options, news…
  • Yaşar Safkan
  • Yasin Saygılı
  • Yazı Dünyası

Yazı Etiketleri

analiz Bilmek C Desen design pattern EJB Eğitim Fortran Hibernate Java Java'ya nasil baslarim Java dersleri Java EE Java Persistence API Java SE Java Sertifika Java Öğren Java öğreniyorum Java öğrenmek JPA Kalıp Kurumsal Java nesne nesne-merkezli No Silver Bullet object object-oriented Oracle Java Certifications pattern performans programlama programlama dilleri programlama nedir sertifika singleton tasarım tasarım deseni tasarım desenleri tasarım şablonu yazılım yazılım geliştirme Yazılım Mühendisliği yazılımın doğası yazılımın zorlukları Şablon

↑

© Java Günlüğüm 2025
Powered by WordPress • Themify WordPress Themes
 

Yorumlar Yükleniyor...