Tasarım desenleri konusu nadiren iş ilanlarında gereksinim olarak görünüyor ve bu beni endişelendirmekle beraber şaşırtıyor da. Bu yazıda bütün tasarım desenlerini anlatmam (veya öğrenmem de) imkansız ama bu konuya kafa yormuş araştırmış birisi olarak Udemy 'de hazırladığım eğitimde geçen 14 farklı tasarım desenininden bazılarını ve konunun önemini özetle açıklayabilirim. Eğitimin esas amacı zaten soyut ve mimari kavramlar olan tasarım desenlerini akılda kalıcı örnekler ile anlatmak ve özümsettirmek. Bu yazıda hem tanıtım hem de anlatım yapacağım. Eğitimi burada bulabilirsiniz.
Algoritma, programcıların ne yaptıklarını anlatmak istemedikleri zaman kullandıkları kelimedir. Tasarım deseni ise programcıların ne yaptıklarını anlatmak istedikleri zaman kullandıkları kavramlardır. Başka bir deyiş ile geliştirme aşamasında karşılaşacağınız herhangi bir soruna getirilmiş çözüm yaklaşımıdır, çözümün kendisi değildir. Tasarım deseni size hangi kodu yazacağınızı değil kod mimarisini açıklar.
Çoğunlukla hikayesi bilinir bu yüzden o kısmı atlıyorum. Design pattern neden var diye bir soru soruyor olmalısınız. Programlama dediğimiz şey aslında bilgisayar hafızasında verinin bir şekilde depolanması erişilmesi ve işlenmesidir sonuçta. Bunları yapmak için kodlar yazarız ve bu kodlar zamanla etkileşime girmeye başlar. Bu noktada programlamadan da bağımsız olarak bir yönetime ve herkesin anlayabileceği bir dile ihtiyaç duyulur. Bu dil 3 grupta toplanır. Creational, Behavioral ve Structural. İlki bilginin oluşturulması ile, ikincisi bunun davranışı ile, üçüncüsü ise yapılandırılması ile ilgilenir. Hala çok soyut değil mi. O zaman örnekler gelsin, felsefeyi sona saklayayım. Aşağıdaki listede bazı tasarım desenlerinin can alıcı bazı kodlarını yazdım. Bu kodları neden yazdığım ve daha fazlası ise tabi ki eğitimin konusu :)
Bir creational pattern olan builder adından da anlaşılacağı gibi bir nesnenin bizim için kolaylıkla build edilebilmesini sağlıyor. Bu desen için verdiğim örnek body yani vücut. Bu şekilde bodybuilder sınıfı yazabiliyoruz :) Bir insan vücudunu parça parça oluşturmak için onlarca satır kod yazmak yerine bu nesne üzerinde oynayan bir builder sınıfı ile fluent (akıcı) kod dediğimiz yapıda kod yazabiliyoruz. JavaFX 'te tanışmıştım bu kod stili ile ve bayılmıştım. Aşağıdaki kod, builder sınıfı yapısının sonucunda oluşuyor. Builder her metodda üzerine yeni özellikler kazanıyor sonuçta body 'yi oluşturuyor. Önce 2 kol sonra 2 kulak sonra 2 göz ile lego gibi oluşturabiliyoruz.
Body body = new BodyBuilder().setArms( 2 ).setEars( 2 ).setEyes( 2 ).build();
Factory yani fabrika deseni değişik şekillerde yorumlanabiliyor. Bunun için bir fabrikadan daha yaratıcı bir örnek bulamadım :) Bu fabrikada araba ve motosiklet yapılabiliyor. Builder 'dan daha kompleks olan yapım işlerini bize göstermeden hallediyor. Buradaki amaç karmaşık bir nesne oluşturma işlemini kolaylaştırmaktır. Bir factory sınıfı araba için mesela kapı sayısı beklerken motosiklet için ön ve arka teker kongigürasyonu istiyor mesela. Bu şekilde bana araba yada motosiklet yap demeniz yetiyor, detaylara karışmıyorsunuz. Benim yaklaşımımda bu fabrikayı aşağıdaki gibi tasarladım. Java 'nın generic yapısından da faydalanmış oldum.
public class VehicleFactory
{
public static Vehicle buildVehicle( Class<Car> C, int doorsCount )
{
Vehicle vehicle = new Car( doorsCount );
return vehicle;
}
public static Vehicle buildVehicle( Class<Bike> B, String frontTyre, String rearTyre )
{
Vehicle vehicle = new Bike( frontTyre, rearTyre );
return vehicle;
}
}
Bundan önceki tasarım desenleri nesnelerin oluşturulması ve hafızada tutulması ile ilgileniyordu. Şimdi sıra davranışsal desenlere geldi. Behaviour yani davranış ile ilgilenen bu tasarım desenleri nesneler arası etkileşimde karşımıza çıkan sorunları çözüyor veya alternatifleri geliştiriyor. Visitor yani ziyaretçi örneğinde benim örneğim postacı. Postacı sınıfı konut yani Residence sınıflarını ziyaret edip bunlardan sonuç alıyor, posta iletildi mi iletilmedi mi şeklinde. Fakat gittiği konutlar bir apartman dairesi olabilir, bir çiftlik olabilir, bir malikane olabilir. Her türlü konutta farklı davranışlar gösterebilen koskoca bir postacı sınıfı yazmak yerine bu konutlar bir interface 'den türetiliyor ve postacı nesnesini kabul edip içindeki yapıya göre sonuç veriyor. Aşağıdaki örnek kodlar açıklayıcı olacaktır. Özetle aşağıdaki postman sınıfından çağırılan isHome metodu çok karmaşık olabilir. Bu yüzden konut sınıfları içerisinde gerçekleştirilmiştir.
class Postman
{
private ArrayList<Boolean> delivered = new ArrayList<>();
public void deliver( Farm farm )
{
delivered.add( farm.isHome() );
}
public void deliver( Apartment apartment )
{
delivered.add( apartment.isHome() );
}
}
public class Apartment implements Residence
{
@Override
public void acceptMail( Postman postman )
{
postman.deliver( this );
}
public boolean isHome()
{
return new Random().nextInt( 5 ) != 3;
}
}
Davranışsal tasarım deseninin tam karşılığı strateji desenidir. Bu desene göre elinizdeki bir nesne hayatta iken farklı davranışlar gösterebilmelidir. Bunun için farklı yetenekleri olmalı ve o zaman kocaman bir sınıfımız mı olmalı? Hayır. Bunu dependency injection konsepti üzerine kurgulayabiliriz. Benim verdiğim örnek bir tiyatro oyuncusu örneği. Oyuncuya hangi temada oynayacağını söylüyorsunuz yani scripti veriyorsunuz, o da ona göre davranabiliyor. Tabi gerçek hayatta oyuncular zaten bu yeteneğe sahip oluyor ama akılda kalıcılık açısından bu şekilde tutulabilir sanırım. Aşağıdaki Actor sınıfı içerisinde genre yani tema değiştirebilen bir metoda sahip. Bu metod da aynı interface 'den türetilmiş farklı türleri alıp davranışını ona göre değiştirmiş oluyor.
public class Actor
{
private Genre genre;
public void setGenre( Genre genre )
{
this.genre = genre;
}
public void act()
{
genre.act();
}
}
Sorumluluklar zinciri şeklinde çevirebileceğim bu tasarım deseninin çözüm bulduğu konu bir işlevin sıra ile farklı nesneler tarafından yerine getirilebilmesidir. Başka bir deyişle herkes kendi işini yapsın demektir. Gündelik hayatta çok kullanıyoruz bu sözü fakat benim bu desene verdiğim örnek birazcık mide bulandırıcı olabilir :) Sindirim sistemi, kelimenin tam anlamıyla bir sorumluluklar zinciridir çünkü. Ağızda belli bir sindirim yapılır, sonra midede ve bağırsakta tamamlanır. Geri kalanı sindirim ile ilgili olmadığı için konumuz değil :) Bu tasarım deseninde aslında her nesne bir sonrakine otomatik iletim yapıyor ve bu iletimin mantığına kendisi karar veriyor. Dışardan sadece zincir sırasını belirtiyoruz, bir nevi linkedlist yani bağlı liste gibi. Bu şekilde mideden tekrar ağıza gelmeyeceğini garantilemiş oluyoruz.
public static void main( String[] args )
{
Mouth mouth = new Mouth();
Stomach stomach = new Stomach();
Intestine intestine = new Intestine();
mouth.setNextDigestor( stomach );
stomach.setNextDigestor( intestine );
mouth.digest( "Hamburger" );
}
public class Mouth implements Digestor
{
private Digestor nextDigestor;
public void setNextDigestor( Digestor nextDigestor )
{
this.nextDigestor = nextDigestor;
}
@Override
public void digest( String food )
{
System.out.println( "Mouth is digesting " + food );
if ( nextDigestor != null )
{
nextDigestor.digest( food );
}
}
}
Köprü tasarım deseni ise iki taraftaki karmaşayı (veya abstraction 'ı) ortak bir noktada çözen tasarım desenidir. Normalde birbirleri ile etkileşimi çok masraflı olacak olan iki nesne arasında köprü kurmaktadır. Bunun örneği halkla ilişkiler çalışanıdır. Bu kişi patronların çalışma yöntemlerinden anlar ve aynı zamanda gazetecilerin veya sosyal medyanın yönelttiği sorulara cevap verir. Bu kişi isterse başka türden gazetecilerle patronlar arasındaki iletişimi de sağlayabilir. Sonuçta gazeteci ve patron birer şablon yani interface 'dir.
class PublicRelations
{
private CompanyManager companyManager;
private ArrayList<Reporter> reporters;
public PublicRelations()
{
companyManager = new Manager();
reporters = new ArrayList<>();
reporters.add( new SmartReporter() );
reporters.add( new DumbReporter() );
}
public void connect()
{
String question = reporters.get( 0 ).askQuestion();
reporters.get( 0 ).getAnswer( companyManager.makeStatement( 0 ) );
question = reporters.get( 1 ).askQuestion();
reporters.get( 1 ).getAnswer( companyManager.makeStatement( 1 ) );
reporters.get( 0 ).getAnswer( companyManager.makeStatement( 2 ) );
reporters.get( 1 ).getAnswer( companyManager.makeStatement( 2 ) );
}
}
public interface CompanyManager
{
public void work();
public String makeStatement( int i );
}
public interface Reporter
{
public String askQuestion();
public void getAnswer( String message );
}
Başka bir yapısal tasarım deseni olan facade aslında command 'e çok benzeyen bir basitleştirme ve birleştirme desenidir fakat command gibi işleyiş ile değil yapısal özelliklerle ilgilenir. Elinizde birden fazla abstraction yani interface veya sınıf olabilir. Bunların hepsinin detaylarına hakim olarak kullanmak yerine önlerinde hepsi ile etkileşime geçebilen bir facade sınıfı yazabilirsiniz. Bunun örneği yönetici asistanıdır mesela. Birden fazla türden çalışan ile yönetici arasındaki iletişimi basitleştirerek yöneticiye kolaylık sağlamaktadır. Bridge 'e çok benzese de buradaki karmaşa çalışanlar tarafında ve amaç bu karmaşayı tek bir noktadan basitleştirebilmek. Aşağıdaki örnek Work ve Finans interface 'inden türetilen nesneleri kullanabilen bir asistan örneğidir.
public class Assistant
{
private Finance finance;
private ArrayList<Work> works = new ArrayList<>();
public void setFinance( Finance finance )
{
this.finance = finance;
}
public void addWork( Work work )
{
works.add( work );
}
public String getReport()
{
String report = "";
for ( Work work : works )
{
report += work.details() + "\n";
}
return report;
}
public void getFinancialSituation()
{
finance.financialSituation();
finance.financialForecast();
}
}
Bir projenin tasarım deseni hikayesi olmalıdır kodlamaya başlamadan önce. Bunu ben bir ev yapmaya benzetiyorum.
Tasarım desenleri aslında bir programlama anlatım dilidir. Bu yüzden basit ve anlaşılır hale getirilmesi de oldukça zordur. Bu konuyu anlayabilmek için basit seviyede nesne yönelimli programlama bilinmesinde fayda var tabi. Çünkü bazı tasarım desenleri sadece nesne yönelimli dillerde geçerli oluyor veya bazıları sadece structural dillerde de geçerli olabilmekte. Hazırladığım eğitimde çeşitli karşılaştırmalar ve başka tasarım desenleri de bulunuyor. Erişim linkini tekrar koyuyorum. Son olarak da diyorum ki, yazılım bir sanattır doğru yazıldığında. Bir sonraki yazıda görüşmek üzere :)
Bir yorum yazabilirsiniz