https://www.buraksenyurt.com/Burak Selim Şenyurt - Tasarım Kalıpları(Design Patterns)2019-03-19T08:15:47+00:00Matematik Mühendisi Bir Bilgisayar Programcısının NotlarıBurak Selim SenyurtBlogEngine.Net Syndication Generatorhttps://www.buraksenyurt.com/opml.axdBurak Selim SenyurtMatematik Mühendisi Bir Bilgisayar Programcısının Notlarıtr-TRBurak Selim Şenyurt0.0000000.000000https://www.buraksenyurt.com/post/specification-tasarim-kalibina-gitmeye-calisirken-benSpecification Tasarım Kalıbına Gitmeye Çalışırken Ben2016-09-20T21:30:00+00:00bsenyurt<p><img style="float: right;" src="https://www.buraksenyurt.com/image.axd?picture=/2016/09/istebeyle2.gif" alt="" />Merhaba Arkadaşlar,</p>
<p>Şu sıralar üzerinde çalışmakta olduğumuz bir projede karşılaştığımız bir sorun var. Belli bir Domain içerisinde yer alan bazı varlıkların<em>(Entity türleri diyelim)</em> çeşitli kriterlere uyanlarının liste olarak çekilmesi gerekiyor. Senaryonun ilginçleştiği kısım ise farklı Entity tipleri için zaman içerisinde farklı kriterlerin de sisteme dahil edilmek istenebileceği. Bu sayede veri kümesi üzerinde çeşitli araştırma senaryolarını denemek de mümkün hale geliyor. Bir başka deyişle aklımıza geldikçe yeni bir kriteri<em>(örneğin bir filtreleme ölçütünü)</em> tanımlayıp istediğimiz Entity kümeleri üzerinde kullanmak istiyoruz.</p>
<p>Konu bir süre sonra sıkıcı hale gelmeye başlayınca pek tabii bilinen yazılım kalıpları ile çözülebilir mi diye de araştırmaya başladım<em>(Nasıl araştırdım derseniz yandaki şekle bakabilirsiniz)</em> Aklıma gelen bazı çözümler vardı ve nihayet sonunda kendimi Specification tasarım kalıbını araştırırken buldum. <a href="https://en.wikipedia.org/wiki/Specification_pattern" target="_blank">Wikipedia' daki benim için karmaşık olan örnek</a> ve Master Martin Fowler'ın konu ile ilgili yazısını takiben basit bir kod parçası araştırarak geçirdiğim bunaltıcı saatlerden sonra konuyu kendimce yazıya dökerek açıklayabilirim diye düşündüm.</p>
<p>İlk olarak sorunu masaya yatırmaya çalışalım. Elimizde aşağıdaki gibi bir sınıf olduğunu düşünelim. Bu sınıf yardımı ile belli bir domain içerisindeki müşterileri temsil ettiğimizi varsayabiliriz.</p>
<pre class="brush:csharp;auto-links:false;toolbar:false" contenteditable="false">public class Customer
{
public int CustomerID { get; set; }
public string Fullname { get; set; }
public decimal Salary { get; set; }
public string City { get; set; }
}</pre>
<p>Kuvvetle muhtemel müşteri bilgileri bir veri kaynağından beslenecektir. Burada sadece müşteri kümesini temsil etmesi açısından eklenmiştir. Şimdi geliştirmekte olduğumuz uygulamanın şöyle bir ihtiyacı olduğunu düşünelim; Istanbul'da yaşayan müşterileri görmek istiyoruz. Aslında şehrin adının çok önemi yok. Ankara'da ikamet eden müşterilerimizi de görmek isteyebiliriz. Ayrıca maaşı belli bir değer aralığında olanları da görmek isteyebiliriz. Kısacası belirli kriterlere uyan müşteri kümelerini çekmek istediğimiz operasyonlarımız olduğunu düşünelim. Bu durumda aşağıdakine benzer bir sınıf tasarlamak aklımıza gelecek ilk çözümlerden birisidir.</p>
<pre class="brush:csharp;auto-links:false;toolbar:false" contenteditable="false">public class CustomerAnalyzer
{
public List<Customer> GetCustomerByCity(string city)
{
throw new NotImplementedException();
}
public List<Customer> GetCustomerNameContains(string letter)
{
throw new NotImplementedException();
}
}</pre>
<p><em>(Örnek kod parçası sadece gösterim amacıyla yazıldığından metod içlerinde bilinçli olarak NotImplementedException istisnası fırlatılmıştır)</em></p>
<p>Aslında gayet anlaşılır görünüyor öyle değil mi? Fakat duayenlere göre müşterilerimizi araştırmak adına yeni bir fonksiyonellik söz konusu olursa CustomerAnalyzer sınıfına yeni bir metod daha eklememiz gerekecektir. Oysa ki, kalıp olarak daha efektif bir çözüm olmalı. Öncelikle CustomerAnalyzer sınıfına ait karmaşıklığı ortadan kaldırmamız ve daha sonradan eklenebilecek filtreleme kriterleri için gevşek bağlı<em>(Loosely Coupled)</em> bir yapı tasarlamaya çalışmalıyız. Bunu gerçekleştirmek için işe çeşitli tipte kriterler için genel bir şablon tanımlayarak başlayabiliriz. Aşağıdaki kod parçasında yer alan CustomerSpecification sınıfı bu amaçla tasarlanmıştır.</p>
<pre class="brush:csharp;auto-links:false;toolbar:false" contenteditable="false">public abstract class CustomerSpecification
{
public abstract bool IsSatisfiedBy(Customer customer);
}
public static class CustomerAnalyzer
{
// Bu Customer listesinin bir şekilde bir yerlerden dolduğunu düşünelim
private static List<Customer> customers = new List<Customer>();
public static List<Customer> GetCustomerBySpecification(CustomerSpecification spec)
{
foreach (var customer in customers)
{
if (spec.IsSatisfiedBy(customer))
customers.Add(customer);
}
return customers;
}
}</pre>
<p>CustomerSpecification isimli abstract sınıf tek bir metod içermektedir. IsSatisfiedBy<em>(ki bu ismi bu desenin anlatıldığı kaynaklarda sıklıkla görmekteyiz)</em> metodunun en önemli özelliği geriye bool değer döndürüp parametre olarak Customer tipinden bir değişken almasıdır. Bu sınıf tahmin edeceğiniz üzere yeni bir şartname için gerekli taban arayüzü<em>(her ne kadar henüz bir interface olmasa da)</em> tanımlamaktadır. Yani müşteri kümesine uygulanacak herhangi bir filtre için bu sınıftan türeyen bir tipin tasarlanması yeterlidir. Bu anlamda CustomerAnalyzer sınıfının yapısı değişmiştir. Dikkat edileceği üzere GetCustomerBySpecification metodu bir kriter almaktadır. Söz konusu kriter CustomerSpecification tipinden türeyen bir sınıf örneğidir. Metod içerisinde bu kritere uyan müşterilerin listeye eklenerek döndürülmesi işlemi söz konusudur<em>(LINQ-Language INtegrated Query tarafında da buna benzer yapılar olduğu mutlaka aklınıza gelmiştir. Orada da benzer prensiplerin uygulandığını söyleyebiliriz)</em></p>
<p>Şimdi yeni bir kriter eklemek istediğimizde tek yapmamız gereken yeni bir şartname hazırlamak ve bunu CustomerAnalyzer tipinde ele almaktır. Örneğin belirli bir şehirde yaşayan müşterilerin tespiti için aşağıdaki gibi bir şartname hazırlanır.</p>
<pre class="brush:csharp;auto-links:false;toolbar:false" contenteditable="false">public class CustomerCitySpecification
:CustomerSpecification
{
private string _city;
public CustomerCitySpecification(string city)
{
_city = city;
}
public override bool IsSatisfiedBy(Customer customer)
{
return customer.City.ToUpper() == _city.ToUpper();
}
}</pre>
<p>Maaşı belirli bir değer aralığında olan müşterileri mi almak istiyoruz? O zaman yeni bir şartname hazırlayarak ilerleyebiliriz. </p>
<pre class="brush:csharp;auto-links:false;toolbar:false" contenteditable="false">public class CustomerSalarySpecification
:CustomerSpecification
{
private decimal _minimum;
private decimal _maximum;
public CustomerSalarySpecification(decimal minimum,decimal maximum)
{
_minimum = minimum;
_maximum = maximum;
}
public override bool IsSatisfiedBy(Customer customer)
{
return (customer.Salary >= _minimum && customer.Salary <= _maximum);
}
}</pre>
<p>Kriterlerin kullanımı da son derece kolay olacaktır.</p>
<pre class="brush:csharp;auto-links:false;toolbar:false" contenteditable="false">var result1=CustomerAnalyzer.GetCustomerBySpecification(new CustomerCitySpecification("İstanbul"));
var result2 = CustomerAnalyzer.GetCustomerBySpecification(new CustomerSalarySpecification(1000, 5000));</pre>
<p>Bu sayede CustomerAnalyzer sınıfı için söz konusu olabilecek ne kadar özelleştirilmiş operasyon varsa kaldırılmış ve daha da önemlisi bu operasyonları sisteme dışarıdan öğretebilir hale getirmiş oluyoruz<em>(Bir nevi plug-in tabanlı bir ortama doğru ilerlediğimizi ifade edebiliriz)</em> Yaptığımız örnek farklı ihtiyaçlar için de değerlendirilebilir. Örneğin Domain Driven Design içerisinde Entity'lerin belirli kriterlere göre doğrulanması gerektiği hallerde ele alınabilir. Bu yaklaşım biraz daha profesyonel olarak ele alındığında ortaya mimari kalıplar arasında yer alan <strong>Specification Design Pattern</strong> çıkmaktadır.</p>
<p>C# tarafında generic mimarinin ve Interface kullanımının da işe katılması halinde çözüm daha da zenginleştirilebilir. Nitekim söz konusu örnekte bazı handikaplar vardır. Çözüm sadece Customer tipi için söz konusudur. T türünden bir Entity tipi için benzer senaryo inşa edilmek istenebilir. Bu durumda C#'ın generic nimetlerinden de yararlanabiliriz. Hatta şartname bir arayüz(Interface) olarak da tasarlanabilir. Nitekim bu arayüzden türetmeler yapılarak kompozit şartnamelerin hazırlanması<em>(and, or gibi mantıksal birleştirme yaklaşımlarını içeren)</em> ve zincir şeklinde metod kullanımlarına imkan tanınarak aynı entity için ardışıl kriterlerin entegre edilmesi mümkün hale getirilebilir<em>(Bu karmaşık cümleyi daha iyi anlamak için <a href="https://en.wikipedia.org/wiki/Specification_pattern" target="_blank">wikipedia adresine</a> bakmanızı öneririm)</em></p>
<p><img src="https://www.buraksenyurt.com/image.axd?picture=/2016/09/specpttrn_1.gif" alt="" /></p>
<pre class="brush:csharp;auto-links:false;toolbar:false" contenteditable="false">public interface ISpecification<T>
{
bool IsSatisfiedBy(T entity);
}
public static class CustomerAnalyzer
{
// Bu Customer listesinin bir şekilde bir yerlerden dolduğunu düşünelim
private static List<Customer> customers = new List<Customer>();
public static List<Customer> GetCustomerBySpecification(ISpecification<Customer> spec)
{
foreach (var customer in customers)
{
if (spec.IsSatisfiedBy(customer))
customers.Add(customer);
}
return customers;
}
}
public class CustomerCitySpecification
:ISpecification<Customer>
{
private string _city;
public CustomerCitySpecification(string city)
{
_city = city;
}
public bool IsSatisfiedBy(Customer customer)
{
return customer.City.ToUpper() == _city.ToUpper();
}
}
public class CustomerSalarySpecification
:ISpecification<Customer>
{
private decimal _minimum;
private decimal _maximum;
public CustomerSalarySpecification(decimal minimum,decimal maximum)
{
_minimum = minimum;
_maximum = maximum;
}
public bool IsSatisfiedBy(Customer customer)
{
return (customer.Salary >= _minimum && customer.Salary <= _maximum);
}
}</pre>
<p>Tabii konunun en detaylı ve ilmi açıklaması <a href="http://martinfowler.com/apsupp/spec.pdf" target="_blank">bu adresteki dokümanda</a> Martin Fowler tarafından yapılmıştır. Böylece geldik bir maceramızın daha sonuna. Her şey DB tarafından çekilen bir nesne topluluğu üzerinde bugün bilinen ama yarın çoğalabilecek farklı filtreleme kriterlerini nasıl ele alabileceğimi araştırmakla başlamıştı. Ben bir şeyler daha öğrendim, ufkum genişledi. Umarım sizler için de faydalı olmuştur. Bir başka makalede görüşünceye dek hepinize mutlu günler dilerim.</p>2016-09-20T21:30:00+00:00design patternarchitecture design patternsoftware design patternsmartin fowlerinterfaceabstract classc#bsenyurtŞu sıralar üzerinde çalışmakta olduğumuz bir projede karşılaştığımız bir sorun var. Belli bir Domain içerisinde yer alan bazı varlıkların(Entity türleri diyelim) çeşitli kriterlere uyanlarının liste olarak çekilmesi gerekiyor. Senaryonun ilginçleştiği kısım ise farklı Entity tipleri için zaman içerisinde farklı kriterlerin de sisteme dahil edilmek istenebileceği. Bu sayede veri kümesi üzerinde çeşitli araştırma senaryolarını denemek de mümkün hale geliyor. Bir başka deyişle aklımıza geldikçe yeni bir kriteri(örneğin bir filtreleme ölçütünü) tanımlayıp istediğimiz Entity kümeleri üzerinde kullanmak istiyoruz.https://www.buraksenyurt.com/pingback.axdhttps://www.buraksenyurt.com/post.aspx?id=4e1bf818-9d82-46f1-8d4d-cb36230c622a9https://www.buraksenyurt.com/trackback.axd?id=4e1bf818-9d82-46f1-8d4d-cb36230c622ahttps://www.buraksenyurt.com/post/specification-tasarim-kalibina-gitmeye-calisirken-ben#commenthttps://www.buraksenyurt.com/syndication.axd?post=4e1bf818-9d82-46f1-8d4d-cb36230c622ahttps://www.buraksenyurt.com/post/business-delegate-patternBusiness Delegate Pattern2016-05-02T06:00:00+00:00bsenyurt<p><img style="float: right;" src="https://www.buraksenyurt.com/image.axd?picture=/2016/05/Bdpattern_4.gif" alt="" />Merhaba Arkadaşlar,</p>
<p>Epey zamandır tasarım kalıpları tarafına bakmadığımı fark ettim. Hem kalıpları tekrar etmek hem de yeni bir şeyler var mı diye internette gezinirken <strong>JEE </strong>tarafında sıklıkla başvurulan <strong>Business Delegate</strong> isimli bir desene rastladım. Aslında delegate dediğimiz zaman bir işi başkasına devrettiğimizi düşünebiliriz <em>(Delegasyon ile ilgili olarak internette resim ararken de işte yandaki gibi eğlenceli bir tanesine rastladım)</em></p>
<p>Normal şartlarda standart olarak kabul gören <strong>Gangs of Four</strong><em>(Erich Gamma, Richard Helm, Ralph Johnson ve John Vlissides)</em> desenlerinde yer almıyor ancak genel yazılım tasarım kalıpları içerisinde yer verilmiş. <em>(Tüm deseneler ile ilişkili olarak <a href="https://en.wikipedia.org/wiki/Design_Patterns" target="_blank">Wikipedia adresine</a> giderseniz diğer desenler kısmında yer aldığını görebilirsiniz)</em></p>
<blockquote>
<p>GoF un tanımladığı tasarım desneleri haricinde literatüre giren farklı kalıplar da bulunuyor. Bu tip kalıpları araştırmak, hangi problemelere çözüm olarak ele alındıklarını öğrenmek, kişisel gelişim açısından önemli.</p>
</blockquote>
<p><strong>Business Delegate</strong> kalıbı temel olarak <strong>sunum katmanı<em>(presentation layer)</em></strong> ile <strong>iş katmanını<em>(Business Layer)</em></strong> arasındaki iletişimde aynı isimli fonksiyonların ele alınmasında değerlendiriliyor. Zaten adından da anlaşılacağı üzere sunum katmanındaki bir fonksiyonelliğin asıl iş katmanındaki karşılığına devredilmesi söz konusu. Burada önemli noktalardan birisi talebi olan nesnenin talep ile ilgili <strong>içeriği<em><strong>(</strong>Context diyebiliriz)</em></strong> aynen ikinci bir nesneye delege etmesidir. </p>
<p>Teorik olarak aşağıdaki gibi bir çizelge desenin kullanımı ile ilişkili bir ipucu verebilir. Aslında Adapter ve Bridge tasarım kalıpları da tercih edilir. Kullanım alanı olarak .Net tarafındaki <strong>asenkron tabanlı<em>(async-based)</em></strong> programlama ihtiyaçlarında kullanılabilir<em>(Örneğin bir servis çağrısının asenkron olarak gerçekleştirilmesi ve işlemin tamamlanmasını takiben geriye dönen <strong>Task</strong>'ın işaret ettiği fonksiyonun çağırılması)</em></p>
<p><img src="https://www.buraksenyurt.com/image.axd?picture=/2016/05/Bdpattern_3.gif" alt="" /></p>
<p>Temel olarak desende yer alan aktörler şöyledir.</p>
<ul>
<li><strong>Caller(Client) :</strong> Talep edilen metod çağrısını gerçekleştiren asıl nesne olarak düşünülebilir. Arayüz tarafındaki bir kod parçası olabilir. Sunum katmanında yer alır.</li>
<li><strong>Business Delegate :</strong> Talepte bulunan nesne çağrılarının asıl iş servisi metodlarına geçişisini sağlayan temsilcidir. Genellikle tek bir giriş noktası<em>(endPoint olarak isimlendirelim)</em> sunar.</li>
<li><strong>LookUp Service :</strong> İlişkili iş nesnelerinin üretiminden sorumludur. Çoğunlukla dışarıdan verilen bilgiye göre <strong>Business Service Interface</strong> türevli bir nesnenin üretimini gerçekleştirir <em>(<strong>Service Locator</strong> gibi de düşünebiliriz)</em></li>
<li><strong>Business Service Interface</strong> : Asıl iş fonksiyonelliklerini bulunduran sınıfların şema tanımlamasını içeren <strong>arayüzdür<em>(Interface)</em></strong>.</li>
</ul>
<p>Şimdi basit bir örnek ile bu deseni incelemeye çalışalım. Aşağıdaki sınıf çizelgesinde yer alan tiplerin yer aldığı <strong>Console</strong> uygulamasını geliştirerek ilerleyebiliriz.</p>
<p><img src="https://www.buraksenyurt.com/image.axd?picture=/2016/05/Bdpattern_1.gif" alt="" /></p>
<pre class="brush:csharp;auto-links:false;toolbar:false" contenteditable="false">using System;
namespace TheBusinessDelegatePattern
{
class Program
{
static void Main(string[] args)
{
BusinessMessagingDelegate bsnDelegate = new BusinessMessagingDelegate();
bsnDelegate.ServiceType = "SMS";
Caller client = new Caller(bsnDelegate);
string smsMesssage = "Son 4 hanesi 1111 olan kartınız dönem borcu -1000 Liradır. Bu kez ödeme bizden.";
string emailMessage = "Son 4 hanesi 1111 olan kartınızı dönem ekstresi ektedir. Açınız şaşırınız.";
client.Do(smsMesssage);
bsnDelegate.ServiceType = "EMAIL";
client.Do(emailMessage);
}
}
public interface IMessagingService
{
void SendMessage(string message);
}
public class SMSMessagingService
: IMessagingService
{
public void SendMessage(string message)
{
Console.WriteLine(message);
}
}
public class MailMessagingService
: IMessagingService
{
public void SendMessage(string message)
{
Console.WriteLine(message);
}
}
public class BusinessServiceLookUp
{
public IMessagingService GetBusinessService(String serviceType)
{
if (serviceType.Equals("SMS"))
{
return new SMSMessagingService();
}
else
{
return new MailMessagingService();
}
}
}
public class BusinessMessagingDelegate
{
BusinessServiceLookUp lookupService = new BusinessServiceLookUp();
IMessagingService businessService;
public string ServiceType { get; set; }
public void Do(string message)
{
businessService = lookupService.GetBusinessService(ServiceType);
businessService.SendMessage(message);
}
}
public class Caller
{
BusinessMessagingDelegate _businessService;
public Caller(BusinessMessagingDelegate businessService)
{
_businessService = businessService;
}
public void Do(string message)
{
_businessService.Do(message);
}
}
}</pre>
<p> </p>
<p>Dilerseniz kodda neler yaptığımızı kısace değinelim. Temel olarak <strong>SMS</strong> ve <strong>EMail</strong> gönderim işlemlerinin ele alındığını mesajlaşma hizmetleri söz konusu. <strong>SMSMessagingService</strong> ve <strong>MailMessagingService</strong> sınıflarının ortak özelliği <strong>IMessagingService</strong> arayüzünden türemiş olmaları. Bu sınıfların iş katmanındaki asıl tipler olduğunu varsayabiliriz.</p>
<p><strong>Caller</strong> sınıfı talepte bulunan arayüz nesnes kullanıcısı olarak düşünülebilir. Arayüz katmanında olduğunu varsaydığımız bu tipin önemli kabiliyetlerinden birisi, hangi iş hizmetini kullanacağını bilmesidir. Bunun için <strong>yapıcı metoduna<em>(Constructor)</em> </strong>parametre olarak gelen <strong>BusinessMessagingDeletage</strong> tipinden yararlanır. Bu tipin <strong>ServiceType</strong> özelliği, delegasyonun yapılacağı iş biriminin üretiminde kullanılır. Üretim aşamasında ise <strong>BusinessServiceLookUp</strong> isimli sınıf devreye girmekte ve <strong>BusinessMessageDelegate</strong> sınıfının <strong>ServiceType</strong> özelliğine göre bir <strong>IMessagingService</strong> uyumlu referansı kullanımı için vermektedir.</p>
<p>Çalışan program kodunda <strong>Caller</strong> nesne örneği üzerinden <strong>Do</strong> metoduna gerçekleştirilen iki farklı çağrı söz konusudur. Kod temsilcinin üretim biçimine göre uygun olan iş birimi hizmetine yönlenir. Kodun çalışma zamanı çıktısı aşağıdaki ekran görüntüsündeki gibi olacaktır.</p>
<p><img src="https://www.buraksenyurt.com/image.axd?picture=/2016/05/Bdpattern_2.gif" alt="" /></p>
<p>Pek tabii gerçek hayat senaryolarında asıl iş birimi metodlarının <strong>string</strong> parametre yerine bir içerik tipi ile<em>(örneğin <strong>Context</strong> isimli bir sınıf)</em> çalışması ve geriye bir referans döndürmesi muhtemeldir. Hatta bu tipler genellikle sunum ve iş katmanı arasında hareket eden transfer nesneleri de olabilir.</p>
<p>Bu yazımızda pek göz önünde olmayan tasarım kalıplarından birisine değinmeye çalıştık. Bir başka makalemizde görüşünceye dek hepinize mutlu günler dilerim.</p>2016-05-02T06:00:00+00:00design patternssoftware design patternsgofservice locatorinterfaceinheritancebridgeadapterc#bsenyurtEpey zamandır tasarım kalıpları tarafına bakmadığımı fark ettim. Hem kalıpları tekrar etmek hem de yeni bir şeyler var mı diye internette gezinirken JEE tarafında sıklıkla başvurulan Business Delegate isimli bir desene rastladım. Aslında delegate dediğimiz zaman bir işi başkasına devrettiğimizi düşünebiliriz (Delegasyon ile ilgili olarak internette resim ararken de işte yandaki gibi eğlenceli bir tanesine rastladım) Business Delegate kalıbı temel olarak sunum katmanı(presentation layer) ile iş katmanını(Business Layer) arasındaki iletişimde aynı isimli fonksiyonların ele alınmasında değerlendiriliyor. Zaten adından da anlaşılacağı üzere sunum katmanındaki bir fonksiyonelliğin asıl iş katmanındaki karşılığına devredilmesi söz konusu. Burada önemli noktalardan birisi talebi olan nesnenin talep ile ilgili içeriği(Context diyebiliriz) aynen ikinci bir nesneye delege etmesidir.https://www.buraksenyurt.com/pingback.axdhttps://www.buraksenyurt.com/post.aspx?id=46147440-d710-4d75-8210-31c6525460705https://www.buraksenyurt.com/trackback.axd?id=46147440-d710-4d75-8210-31c652546070https://www.buraksenyurt.com/post/business-delegate-pattern#commenthttps://www.buraksenyurt.com/syndication.axd?post=46147440-d710-4d75-8210-31c652546070https://www.buraksenyurt.com/post/Design-Patterns-Template-MethodTasarım Desenleri – Template Method2015-02-03T19:00:00+00:00bsenyurt<p><a href="https://www.buraksenyurt.com/pics/eskiici.jpg"><img style="margin: 4px 0px; display: inline; float: right;" title="eskiici" src="/pics/eskiici_thumb.jpg" alt="eskiici" width="280" height="210" align="right" /></a>Merhaba Arkadaşlar,</p>
<p>Düzenli olarak teknik paylaşımlarda bulunan internet yazarlarının karşılaştığı en büyük sorunlardan birisi, hızla gelişen teknoloji nedeniyle ele alınan konuların kolayca eskimesidir. Hangi firma olursa olsun bu kural geçerlidir. Bu eskitme işinde elbette başı çeken bir kaç firma var. Zaman zaman yazarların serzenişte bulunup kızdığı Microsoft, Oracle, Google ve diğerleri. </p>
<p>Dolayısıyla yazdığımız yazılar bir süre sonra eskiyen gaz lambaları misali duvarın bir köşesine asılıp yavaş yavaş çürüyorlar. Elbette istisnai durumlar da söz konusu. Nitekim pek çok firma<span style="font-style: italic;">(örneğin finans kurumları) </span>teknolojiyi bazen geriden takip etmekte. O nedenle yazılan içeriğin hala bir yerlerde birilerinin işine yarayacağını ümit edebiliriz.</p>
<p>Pek tabi teknik yazıların bir yaşam ömrüne sahip olması, kalıcı olan içerik sayısının daha kıymetli olmasına neden olmaktadır. Söz gelimi bir programlama dilinin temel özellikleri, dilden bağımsız düşünülebilen matematik algoritmaları gibi mevzular kolay kolay eskimezler. Hatta eskimeyen konuların başında <strong>tasarım kalıpları<em>(Design Patterns)</em> </strong>gelir.</p>
<p>Uzun bir süre önce <a href="https://www.buraksenyurt.com/?tag=/design+patterns" target="_blank">tasarım kalıpları konusundaki çalışmalara</a> hem yazılı hem de görsel materyaller ile başlamıştım. Ancak geçtiğimiz gün yaşadığım bir olay nedeniyle bu köşeyi eksik bıraktığımı fark ettim. İşte bu yazımızda <strong>davranışsal tasarım kalıplarından<em>(Behavioral Design Patterns)</em></strong> olan <strong>Template Method</strong> desenini incelemeye çalışacağız.</p>
<p>Aslında deseni ele almaya karar vermem de etkili olan faktor, bir süredir incelediğim <strong>Anti-Pattern</strong> konusudur. <a href="http://en.wikipedia.org/wiki/Anti-pattern" target="_blank">Wikipedia</a> daki kaynaktan <strong>Object Oriented Programming</strong> başlığı altındaki <strong>Sequential Coupling</strong> konulu <strong>anti-pattern</strong> maddesini incelerken, çözüm yolu olarak<strong> Template Method</strong> deseninin önerildiğini fark etmiştim.</p>
<h1>Tanımlama</h1>
<p><strong>Template Method</strong> tasarım kalıbı daha çok sıralı operasyonları içeren fonksiyonellikleri ilgilendirmektedir. Öyleki bu fonksiyonellikler içeisine dahil olan operasyonların bazıları, duruma göre farklı şekillerde uygulanmak istenebilir<em>(Diğer fonksiyonlarda aslında standart olarak hep aynı işi yaparlar)</em> Dolayısıyla fonksiyonelliğin sahiplendiği ve çalışma biçimleri değişkenlik gösterebilecek olan operasyonların kolayca genişletilebilmesi, sahip oldukları <strong>kod parçalarının yeniden kullanılabilirliğinin arttırılması<em>(Code Reusability)</em></strong> noktasında bir çözüm gerekliliği ortaya çıkmaktadır. <strong>Template Method </strong>tasarım kalıbı burada çözüm olarak kullanılabilir. Buna göre kalıbı şu şekilde tanımlayabiliriz,</p>
<blockquote>
<p>Bir algoritmanın sıralı parçalarını oluşturan operasyonlardan değişime açık olanlarının alt sınıflarda(Sub Classes) implemente edilmek suretiyle ele alınmasını öngeren bir desendir.</p>
</blockquote>
<h1>Sınıf Çizelgesi</h1>
<p>Tasarım kalıbının sınıf çizelgesi aşağıdaki gibi düşünülebilir.</p>
<p><a href="https://www.buraksenyurt.com/pics/tmdp_1.png"><img style="margin: 4px 0px; display: inline;" title="tmdp_1" src="/pics/tmdp_1_thumb.png" alt="tmdp_1" width="603" height="355" /></a></p>
<p>Dikkat edilmesi gereken operasyon <strong>TemplateMethod</strong> isimli fonksiyondur. Bu fonksiyon içerisinde sırasıyla çalıştırılan başka alt fonksiyonlar bulunmaktadır. Bunlar sırasıyla <strong>OperationZ</strong>, <strong>OperationA</strong>, <strong>OperationY</strong>, <strong>OperationF</strong> ve <strong>OperationB</strong> dir. <strong>OperationA</strong> ve <strong>OperationB</strong> isimli fonksiyonlar ise <strong>abstract</strong> olarak tanımlanmışlardır ve uygulanışları <strong>Concrete</strong> sınıflar da gerçekleştirilmektedir. Bir başka deyişle <strong>OperationA</strong> ve <strong>OperationB</strong> için davranışsal bir genişletme imkanı söz konusudur.</p>
<p><strong>N sayıda</strong> <strong>Concrete</strong> sınıf söz konusu olabilir. Çalışma zamanında, tercih edilen <strong>Concrete</strong> sınıf hangisi ise, <strong>TemplateMethod</strong> içerisindeki dizilim ilgili <strong>Concrete</strong> sınıfın <strong>OperationA</strong> ve <strong>OperationB</strong> metodlarını kullanacaktır. <strong>OperationA</strong> ve <strong>OperationB</strong> dışında kalan metodlar aslında sabit olan, bir başka deyişle çalışma şekillerinde her hangi bir değişiklik bulunmayacak fonksiyonellikleri temsil etmektedir.</p>
<h1>Örnek Senaryo</h1>
<p>Çok doğal olarak konuyu anlamanın en kolay yolu basit bir örnek ile mümkün olabilir. Bu amaçla şu senaryoyu göz önüne alabiliriz;</p>
<p>Bir oyun programında kullanıcı istatistiklerinin özet olarak raporlandığını düşünelim. Bu raporlama işini üstlenen bir sınıf söz konusu olsun. Sınıfın Template Method olarak düşünülecek ilgili fonksiyonelliğinin ise çalıştırdığı bir metod zinciri bulunsun. Bu zincirde yer alan fonksiyonelliklerden oyuncu bilgisinin toplanması ve ayrıştırılması değişmez parçalar olmak üzere iki ayır metod şeklinde tasarlansın. Ancak en son olarak çağırılan ve toplanan içeriğinin bastırılacağı yeri ele alan fonksiyonellik genişletilebilir olsun.</p>
<h1>Sınıf Çizelgesi</h1>
<p>Çok basit olarak aşağıdaki sınıf çizelgesinde yer alan örneği tasarlamamız yeterli olacaktır.</p>
<p><a href="https://www.buraksenyurt.com/pics/tmdp_2.png"><img style="margin: 4px 0px; display: inline;" title="tmdp_2" src="/pics/tmdp_2_thumb.png" alt="tmdp_2" width="632" height="376" /></a></p>
<p><strong>GameReporter</strong> <strong>abstract</strong> sınıfı içerisinde yer alan <strong>WriteSummary</strong> metodunu <strong>Template</strong> <strong>Method</strong> olarak düşünebiliriz. Bu metod kendi içinde sırasıyla <strong>GetResults</strong>, <strong>ParseResults</strong> ve <strong>WriteResults</strong> isimli fonksiyonellikleri çağıracaktır. Dikkat edileceği üzere <strong>WriteResults</strong> bir <strong>abstract</strong> metod olarak tanımlanmıştır ve <strong>GameReporter</strong> tipinden türeyen <strong>TextReporter</strong>, <strong>ConsoleReporter</strong> ve <strong>XmlReporter</strong> sınıflarınca ezilmektedir. Bu tasarıma göre <strong>GameReporter</strong> sınıfının tüketicisine özet bilgiyi yazdırabileceği 3 farklı alternatif sunulmaktadır. Kullanıcı bilgileri isterse <strong>Text</strong> ya da <strong>XML</strong> dosyasına yazabilir veya doğrudan <strong>Console</strong> penceresine bastırabilir. Eğer yeni bir seçenek eklenmesi gerekirse<em>(örneğin sonuçları PDF olarak bastırmak gibi)</em> bu durumda yeni bir <strong>GameReporter</strong> türetmesi yapılması yeterli olacaktır.</p>
<blockquote>
<p>Pek tabi buradaki abstract sınıfın kendisi, uygulayıcıları ve tüketici olan program farklı katmanlarda<em>(projelerde)</em> duruyor olabilir.</p>
</blockquote>
<h1>Kod ve Çalışma Zamanı</h1>
<p>Sınıf çizelgesinde yer alan tipleri aşağıdaki kod parçasında görüldüğü gibi yazabiliriz.</p>
<pre class="brush:csharp;auto-links:false;toolbar:false" contenteditable="false">using System;
namespace ConsoleApplication9
{
class Program
{
static void Main(string[] args)
{
GameReporter reporter = null;
reporter = new XmlReporter();
reporter.WriteSummary();
Console.WriteLine();
reporter = new TextReporter();
reporter.WriteSummary();
Console.WriteLine();
reporter = new ConsoleReporter();
reporter.WriteSummary();
}
}
abstract class GameReporter
{
public void GetResults()
{
Console.WriteLine("Oyuncuların istatistikleri toplanıyor");
}
public void ParseResults()
{
Console.WriteLine("İstatistikler ayrıştırılıyor");
}
public abstract void WriteResults();
public void WriteSummary()
{
GetResults();
ParseResults();
WriteResults();
}
}
class XmlReporter
: GameReporter
{
public override void WriteResults()
{
Console.WriteLine("İstatistikler XML dosyasına yazılıyor.");
}
}
class TextReporter
: GameReporter
{
public override void WriteResults()
{
Console.WriteLine("İstatistikler TEXT dosyasına yazdırılıyor.");
}
}
class ConsoleReporter
: GameReporter
{
public override void WriteResults()
{
Console.WriteLine("İstatistikler CONSOLE ekranına basılıyor.");
}
}
}</pre>
<p><strong>Main</strong> metodu içerisinde <strong>GameReporter</strong> tipinden bir değişken tanımlandığı görülmektedir. Değişken örneklenirken ise <strong>türeyen sınıflar<em>(Derived Class)</em></strong> kullanılmaktadır. Bu son derece doğaldır, nitekim <strong>abstract</strong> tipler çok biçimli olduklarından, kendisinden türeyen sınıfların nesne örneklerini taşıyabilmektedirler<em>(Nesne yönelimli programlama temellerinden Polymorphsym’ i hatırlayalım)</em></p>
<p>Dolayısıyla <strong>reporter</strong> değişkenine bir <strong>GameReporter</strong> nesne örneği atandığında, reporter değişkeni <strong>GameReporter</strong> gibi hareket ediyor olacaktır. Benzer şekilde <strong>reporter</strong> değişkenine bir <strong>XmlReporter</strong> nesne örneği atandığında, <strong>reporter</strong> değişkeni bu kez <strong>XmlReporter</strong> gibi hareket edecektir.</p>
<p>O yüzden çalışma zamanında, türeyen tip içerisinde <strong>ezilmiş<em>(override)</em></strong> olan <strong>WriteResults</strong> metodu devreye girecektir. Nitekim çok biçimlilik, üst tipin kendisinden türeyen alt tip içerisindeki bir fonksiyonelliği yürütebilmesine olanak tanımaktadır.</p>
<p>Uygulamanın çalışma zamanı çıktısı ise aşağıdaki gibidir.</p>
<p><a href="https://www.buraksenyurt.com/pics/tmdp_3.png"><img style="margin: 4px 0px; display: inline;" title="tmdp_3" src="/pics/tmdp_3_thumb.png" alt="tmdp_3" width="588" height="187" /></a></p>
<h1>Uygulamasaydık</h1>
<p>Peki bu şekilde bir yola başvurmasaydık ve söz konusu GameReporter sınıfını örneğin aşağıdaki gibi tasarlasaydık!?(Muhtemelen diyelim)</p>
<pre class="brush:csharp;auto-links:false;toolbar:false" contenteditable="false">using System;
namespace ConsoleApplication9
{
class Program
{
static void Main(string[] args)
{
GameReporter reporter = new GameReporter();
reporter.WriteSummary(Target.Console);
reporter.WriteSummary(Target.TextFile);
reporter.WriteSummary(Target.XmlFile);
}
}
enum Target
{
Console,
XmlFile,
TextFile
}
class GameReporter
{
void GetResults()
{
Console.WriteLine("Oyuncuların istatistikleri toplanıyor");
}
void ParseResults()
{
Console.WriteLine("İstatistikler ayrıştırılıyor");
}
void WriteResults(Target target)
{
switch (target)
{
case Target.Console:
Console.WriteLine("Console a yaz");
break;
case Target.XmlFile:
Console.WriteLine("Xml dosyasına yaz");
break;
case Target.TextFile:
Console.WriteLine("Text dosyasına yaz");
break;
default:
break;
}
}
public void WriteSummary(Target target)
{
GetResults();
ParseResults();
WriteResults(target);
}
}
}</pre>
<p>Neler yaptığımıza bir bakalım dilerseniz.</p>
<p><strong>GameReporter </strong>sınıfı içerisinde yer alan <strong>WriteSummary</strong> metoduna bilgileri yazdırmak istediğimiz hedefi seçebileceğimiz bir <strong>Enum</strong> sabitini parametre olarak geçirmekteyiz. <strong>WriteResults</strong> metodu da bu <strong>Enum</strong> sabitini ele almakta ve çıktıyı seçilen hedefe doğru yönlendirmekte. Temelde bir sorun yok gibi görünüyor. Ancak yeni bir hedefin eklenmesi istendiği noktada<em>(örneğin PDF dosyasına export etmek gibi)</em> mecburen <strong>Enum</strong> sabitini ama daha da önemlisi <strong>WriteResults</strong> metodunun içeriğini değiştirmeliyiz. Zaten bu noktada bir bağımlılık oluştuğunu düşünebiliriz.</p>
<p>Ancak <strong>Template Method</strong> desenini uyguladığımız örnekte <strong>GameReporter</strong> ve diğer alt sınıfların farklı kütüphanelerde durduğunu düşünecek olursak, <strong>GameReporter</strong> tipine bir müdahale de bulunmadan ve daha da önemlisi <strong>WriteSummary</strong> içerisindeki akışı bozmadan, yeni hedefleri ilave etme şansına sahip olduğumuzu fark edebiliriz. Aşağıdaki şekildeki uygulanış biçimi iyi bir çözüm olacaktır.</p>
<p><a href="https://www.buraksenyurt.com/pics/tmdp_4.png"><img style="margin: 4px 0px; display: inline;" title="tmdp_4" src="/pics/tmdp_4_thumb.png" alt="tmdp_4" width="624" height="381" /></a></p>
<p>Buna göre <strong>PDFReporter</strong> şeklinde yeni bir seçenek dahil edilmek istendiğinde <strong>LibraryC</strong> isimli kütüphaneye ilgili sınıfın eklenmesi yeterli olacaktır. <strong>LibraryA</strong> kütüphanesinde yer alan <strong>WriteResults</strong> içerisine müdahale edilmesine gerek yoktur. <strong>Consumer</strong> sınıf ise, sadece yeni eklenen sınıfı örnekleyip kullanmakla yükümlüdür.</p>
<h1>Ödev</h1>
<p>Örnek bir ödev ile yazımızı sonlandıralım. Söz gelimi bir text içeriğinin analiz edilme sürecini göz önüne alalım. Dosyanın okunması, içeriğinin analiz edilerek bazı sonuçlar çıkartılması<em>(Aggregation' lar olabilir)</em> ve son olarak bir yerlere kayıt edilmesini süreç olarak değerlendirelim. Dosyayı okuma ve yazma işlemleri değişmez iken, analiz ve sonuç çıkartma kısımları farklılık gösterebilir. Okuma ve yazma işlemlerini birer operasyon olarak düşündüğümüzde, değişkenlik gösterebilecek fonksiyonellikler analiz ve sonuç üretme kısımlarında ortaya çıkar. Bu senaryoda Template tasarım kalıbını uygulamaya çalışınız. Eğer zorlanırsanız Visual Studio Magazine' de Eric Vogel tarafından yazılan <a href="http://visualstudiomagazine.com/articles/2013/12/06/template--method-pattern-in-dot-net.aspx" target="_blank">şu makaleden yardım alabilirsiniz</a>.</p>
<p>Böylece geldik bir makalemizin daha sonuna. Bu makalemizde davranışsal tasarım kalıplarından birisi olan <strong>Template Method</strong> desenini incelemeye çalıştık. Uygulanışı oldukça basit olan desenin özellikle <strong>Sequential Coupling</strong> isimli <strong>anti-pattern’</strong> in refactor edilmesi noktasında önemli bir yere sahip olduğunu öğrendik. Bir diğer makalemizden görüşünceye dek hepinize mutlu günler dilerim.</p>
<p><a href="https://www.buraksenyurt.com/pics/2013%2f6%2fConsoleApplication9.zip">ConsoleApplication9.zip (73,18 kb)</a></p>2015-02-03T19:00:00+00:00design patternstasarım kalıplarıc#c# temelleriobject oriented programminganti patterntemplate methodbsenyurtDüzenli olarak teknik paylaşımlarda bulunan internet yazarlarının karşılaştığı en büyük sorunlardan birisi, hızla gelişen teknoloji nedeniyle ele alınan konuların kolayca eskimesidir. Hangi firma olursa olsun bu kural geçerlidir. Bu eskitme işinde elbette başı çeken bir kaç firma var. Zaman zaman yazarların serzenişte bulunup kızdığı Microsoft, Oracle, Google ve diğerleri. Pek tabi teknik yazıların bir yaşam ömrüne sahip olması, kalıcı olan içerik sayısının daha kıymetli olmasına neden olmaktadır. Söz gelimi bir programlama dilinin temel özellikleri, dilden bağımsız düşünülebilen matematik algoritmaları gibi mevzular kolay kolay eskimezler. Hatta eskimeyen konuların başında tasarım kalıpları(Design Patterns) gelir.https://www.buraksenyurt.com/pingback.axdhttps://www.buraksenyurt.com/post.aspx?id=9788808a-0830-47c9-b3b1-cdf7a1dabb0b4https://www.buraksenyurt.com/trackback.axd?id=9788808a-0830-47c9-b3b1-cdf7a1dabb0bhttps://www.buraksenyurt.com/post/Design-Patterns-Template-Method#commenthttps://www.buraksenyurt.com/syndication.axd?post=9788808a-0830-47c9-b3b1-cdf7a1dabb0bhttps://www.buraksenyurt.com/post/Fluent-Interface-Prensibi-ile-Daha-Okunabilir-Kod-GelistirmekFluent Interface Prensibi ile Daha Okunabilir Kod Geliştirmek - 1nci Yarı2013-12-23T01:34:00+00:00bsenyurt<p>Merhaba Arkadaşlar,</p>
<p>Keşfedilmesi, anlaşılması ve okunması kolay kod geliştirmek, özellikle dışarıya açık arayüzü bulunan API’ ler için oldukça önemlidir. Bir <strong>Domain Specific Language</strong>’ in olmassa olmazı kodun kolayca keşfedilebilirliğidir. <strong>Ruby</strong> ve <strong>Scala</strong> gibi diller <strong>built-in</strong> olarak bu kolaylığı sunarlar. <strong>LINQ(Language INtegrated Query) </strong>ifadeleri, zincir şeklinde bir birlerine bağlanabilen <strong>Extension</strong> metodlar ile aynı esnekliği vermektedir. Test süreçlerinde kullanılan pek çok <strong>Mock</strong> nesne API’si benzer kabiliyetlere sahiptir. Tüm bunlar aynı prensipten yararlanır. <strong>Fluent Interface</strong>… Bu görsel dersimizde <strong>Martin Fowler</strong> tarafından yıllar önce ortaya konan yaklaşımın uygulanışını incelemeye çalışıyoruz.</p>
<p><iframe src="https://www.youtube.com/embed/lVgz-DeTJhM" width="640" height="360" frameborder="0" allowfullscreen="allowfullscreen"></iframe></p>
<p>Bir başka görsel dersimizde görüşmek üzere.</p>2013-12-23T01:34:00+00:00fluent interfacefluent apimartin fowlerrubyscalamock nesneunit testdomain driven designdomain specific languagedslbsenyurtKeşfedilmesi, anlaşılması ve okunması kolay kod geliştirmek, özellikle dışarıya açık arayüzü bulunan API’ ler için oldukça önemlidir. Bir Domain Specific Language’ in olmassa olmazı kodun kolayca keşfedilebilirliğidir. Ruby ve Scala gibi diller built-in olarak bu kolaylığı sunarlar. LINQ(Language INtegrated Query) ifadeleri, zincir şeklinde bir birlerine bağlanabilen Extension metodlar ile aynı esnekliği vermektedir. Test süreçlerinde kullanılan pek çok Mock nesne API’si benzer kabiliyetlere sahiptir. Tüm bunlar aynı prensipten yararlanır. Fluent Interface… Bu görsel dersimizde Martin Fowler tarafından yıllar önce ortaya konan yaklaşımın uygulanışını incelemeye çalışıyoruz.https://www.buraksenyurt.com/pingback.axdhttps://www.buraksenyurt.com/post.aspx?id=8a442d30-9eb2-4597-a63d-6391ef5eac437https://www.buraksenyurt.com/trackback.axd?id=8a442d30-9eb2-4597-a63d-6391ef5eac43https://www.buraksenyurt.com/post/Fluent-Interface-Prensibi-ile-Daha-Okunabilir-Kod-Gelistirmek#commenthttps://www.buraksenyurt.com/syndication.axd?post=8a442d30-9eb2-4597-a63d-6391ef5eac43https://www.buraksenyurt.com/post/Tasarim-Kaliplari-Intpreter-ile-ikinci-randevuInterpreter Tasarım Kalıbı - İkinci Randevu2009-08-15T17:37:00+00:00bsenyurt<p><img style="float: right;" src="/pics/2009%2f8%2fblg64_Giris_1.jpg" alt="" />Merhaba Arkadaşlar,</p>
<p>Bir süre önce tasarım kalıplarından <a title="Tasarım Kalıpları - Interpreter" href="https://www.buraksenyurt.com/post/Tasarc4b1m-Desenleri-Interpreter.aspx" target="_blank">Interpreter</a> desenini incelemiş ve konu ile ilişkili bir kural motorunun çok basit anlamda nasıl yazılabileceğini araştıracağımızdan bahsetmiştik. Interpreter tasarım kalıbında hatırlayacağınız gibi <strong>Terminal</strong> ve <strong>NonTerminal</strong> tipleri bulunmaktadır. NonTerminal tipler genellikle kural motoru gibi modellerde devreye girmektedir. <strong>Kural motorlarında(Rule Engine),</strong> işletilmek istenen ifadelerin içerisinde sıklıkla operatörlerin kullanılması söz konusudur.</p>
<p>Örneğin <strong>and, or, >=, <, küçüktür, eşittir</strong> gibi düşünebiliriz. Dikkat ederseniz <strong>eşittir</strong> ve <strong>küçüktür</strong> gibi kelimeleri de operatörler arasına kattım. Nitekim yorumlanacak<strong> ifade(Expression)</strong> bütününü kendimiz oluşturduğumuz için istediğimiz terimleri seçmemiz son derece doğaldır. Tam bu noktada sağ üstteki resmin konu ile ne alakası olduğunu düşünebilirsiniz. <img title="Laughing" src="/editors/tiny_mce3/plugins/emotions/img/smiley-laughing.gif" alt="Laughing" border="0" /></p>
<p>Aslında bu yazımızdaki amacımız, içerisinde değişik renklerde misketleri barındıran bir kutu<em>(ki örneğimizde string tipten generic bir koleksiyon olarak ifade edilecek)</em> üzerinde, <strong>string</strong> bazlı mantıksal bir ifadeyi işletmektir. Örnek olarak aşağıdaki gibi bir kural tanımladığımızı göz önüne alabiliriz.</p>
<p><strong>"Kirmizi ve Mavi veya Mor"</strong></p>
<p>Buna göre sepet içerisindeki misketlerin rengine göre yukarıdaki kurala uyan bir durum varsa bir takım işlemlerin yapılmasını veya yapılmamasını arzu ediyoruz. Aslında ne yapılması gerektiğinin şu aşamada bir önemi yok. <img title="Wink" src="/editors/tiny_mce3/plugins/emotions/img/smiley-wink.gif" alt="Wink" border="0" /> Çünkü önemli olan ilk aşama, yukarıdaki kuralı söz konusu misket sepeti üzerinde işletebilmek. Peki bunu nasıl yapacağız? Dahası yapmak için <strong>Interpreter</strong> tasarım kalıbını nasıl kullanacağız?</p>
<p>İlk etapta, kural ifadesi içerisindeki materyalleri göz önüne almamızda yarar var. Renkleri aslında tek bir <strong>Terminal</strong> tipi ile ifade edebiliriz. Nitekim renklerin ayrı ayrı yapacakları bir işlevsellik yok.(<em>Elbetteki kural ifadesi içerisindeki bilgilerin gerçek hayat kural motorlarında ayrı ve farklı görevleri olabilir. Bu durumda her biri için ayrı <strong>Terminal</strong> tiplerinin tasarlanması gerekir)</em> Diğer taraftan renkler arasında <strong>ve, veya</strong> olmak üzere iki mantıksal operatör yer almaktadır. İşte bunlar <strong>NonTerminal</strong> tipler olarak tasarlanmalıdır. Nitekim kendi içlerinde, <strong>Expression</strong> tiplerinden ikisini taşıyacaklardır ki mantıksal olarak <strong>ve, veya</strong> işlemleri gerçeklenebilsin. Tabi birde içerisinde parantezler bulunmayan bir kural ifadesi ile karşı karşıyayız. Kuralın</p>
<p><strong>"Kirmizi ve (Mavi veya Mor)"</strong> olması ile</p>
<p><strong>"(Kirmizi ve Mavi) veya Mor"</strong></p>
<p>olmasının arasında işlem öncelikleri açısından farklılıklar bulunur. Önce parantez içlerini çalıştırmak gerekir. Tabi bizim örneğimizde parantezleri işin içerisine şu an için katmıyor olacağız. Ama size parantezleri işin içerisine katarak geliştirme yapmaya çalışmanızı şiddetle öneririm. Özellikle string biçimdeki kuralı ayrıştırırken çok zorlu bir yoldan geçeceğinizi garanti edebilirim. Öyleki kuralı bir arkadaşınız yanlışlıkla şöylede yazabilir.</p>
<p>"(Kirmizi ve <strong>((</strong>Mavi veya Mor)"...Upsss! <img title="Wink" src="/editors/tiny_mce3/plugins/emotions/img/smiley-wink.gif" alt="Wink" border="0" /></p>
<p>Peki biz kuralı nasıl ayrıştıralım. Aşağıdaki şekil bize bu anlamda bir fikir verebilir.</p>
<p><img src="/pics/2009%2f8%2fblg64_ColorSchema.gif" alt="" /></p>
<p>Aslında bunun programatik taraftaki karşılığını bir <strong>ifade ağacı(Expression Tree)</strong> olarak düşünebiliriz. Ancak yazacağımız kod içerisinde <strong>Interpreter</strong> tasarım kalıbının uygulanması dışında, bu şekilde bir ifade ağacının çıkartılabilmesi için <strong>Recursive</strong> bir fonksiyonada ihtiyacımız olacaktır. Ta ta ta taaa...<img title="Sealed" src="/editors/tiny_mce3/plugins/emotions/img/smiley-sealed.gif" alt="Sealed" border="0" /></p>
<p><em>(<strong>Kişisel Notum :</strong> Uzun yıllar çalıştığım eğitim firmasında verdiğim .Net derslerinde, Recursive metodları anlatırken çoğunlukla Faktoryel hesabı veya Fibonacci sayılarının bulunması problemlerini dile getirdiğimi hatırlıyorum da...Gerçek hayat çok ama çok daha farklı...Geniş düşünmek, vizyonu her zaman geniş tutmak gerekiyor. Çoğu zaman göz ardı ettiğiniz bir kavram, aslında bir problemin çözümünde kritik bir rol üstlenebiliyor. Recursive bir metodun örneğimizdeki ifade ağacının çıkartılmasında üstlendiği rolde olduğu gibi...)</em></p>
<p>Çünkü ifadenin n sayıda renk ve mantık operatörü içermesi söz konusudur. Bu durumda ifade ağacı oluşturulurken ve çalıştırılırken, bir önceki ifadeyi üreten ve bunu sonraki ifadeyi üretmek için girdi olarak kullanan bir fonksiyon yazılması şarttır. Artık örneğimizi geliştirmeye ne dersiniz? Şimdi aşağıdaki sınıf diagramı ve kodları içeren Console uygulamasını yazdığımızı düşünelim.</p>
<p><img src="/pics/2009%2f8%2fblg64_ClassDiagram.gif" alt="" /></p>
<p> </p>
<pre class="brush:csharp;auto-links:false;toolbar:false" contenteditable="false">using System;
using System.Collections.Generic;
namespace Interpreter
{
// Expression Type
abstract class RuleExpression
{
public abstract bool Interpret(List<string> context);
}
#region Terminal Expression Types
class ArgumentExpression
: RuleExpression
{
public string Name { get; set; }
public override bool Interpret(List<string> context)
{
if(context.Contains(Name))
return true;
else
return false;
}
}
#endregion
#region NonTerminal Expression Types
class AndExpression
: RuleExpression
{
public RuleExpression Left { get; set; }
public RuleExpression Right { get; set; }
public override bool Interpret(List<string> context)
{
return Left.Interpret(context) && Right.Interpret(context);
}
}
class OrExpression
: RuleExpression
{
public RuleExpression Left { get; set; }
public RuleExpression Right { get; set; }
public override bool Interpret(List<string> context)
{
return Left.Interpret(context) || Right.Interpret(context);
}
}
#endregion
// Expression ağacını oluşturmak ve çaşlıştırmakla görevli olan sınıf
class RuleComputer
{
public List<RuleExpression> Expressions { get; set; }
public RuleComputer()
{
Expressions = new List<RuleExpression>();
}
// Expression ağacının oluşturucusu ve çalıştırıcısı olan metoddur
public bool RunExpressionTree(string ruleSyntax,List<string> context)
{
bool result = false;
// Önce kural metni içerisindeki boşluklara göre elemanlar ayrılır
string[] ruleParts = ruleSyntax.Split(' ');
// Küçük bir kontrol. Ancak fazlasınıda yapmak gerekir :) Yazılan kural metninin geçerli olup olmadığı denetlenmelidir.
if (ruleParts.Length < 3)
throw new Exception("Eleman sayısı kural için yeterli değildir");
// Expression Tree oluşturulmasına başlanır(Recursive fonksiyonu kullandığımıza dikkat edelim)
RuleExpression longExpression = Recursive(ruleParts, 1, null);
// Expression ağacı koleksiyona eklenir
Expressions.Add(longExpression);
// Koleksiyondaki her bir Expression için Interpret operasyonu çalıştırılır
foreach (RuleExpression expression in Expressions)
{
result = expression.Interpret(context);
}
return result;
}
// Expression ağacının oluşturulması için kullanılan recursive fonksiyon
// Kuralı işletmek için en soldaki ikili daldan başlayarak sağa doğru ilerliyoruz
RuleExpression Recursive(string[] parts, int step, RuleExpression expression)
{
if (step == 1) // Soldan ilk operatör ile karşılaşıldığında
{
if (parts[step] == "ve")
{
expression = new AndExpression { Left = new ArgumentExpression { Name = parts[step - 1] }, Right = new ArgumentExpression { Name = parts[step + 1] } };
}
if (parts[step] == "veya")
{
expression = new OrExpression { Left = new ArgumentExpression { Name = parts[step - 1] }, Right = new ArgumentExpression { Name = parts[step + 1] } };
}
}
else // İlk çift içerisindeki operator geçildikten sonra, her zaman bir önceki dalın, sonradan gelen argüman ile ve/veya işlemine sokulması sağlanır
{
if (parts[step] == "ve")
{
expression = new AndExpression { Left = expression, Right = new ArgumentExpression { Name = parts[step + 1] } };
}
if (parts[step] == "veya")
{
expression = new OrExpression { Left = expression, Right = new ArgumentExpression { Name = parts[step + 1] } };
}
}
// Recursive metoddan bir notkada çıkılması gerekecektir. Bu çıkış noktası, son operatör ele alındıktan sonrasıdır.
if (step == parts.Length - 2)
return expression;
// Öteleme yapılarak sonraki çifti almak üzere aynı metod tekrar işletilir
return Recursive(parts, step + 2, expression);
}
}
class Program
{
static void Main(string[] args)
{
// Örnek kural
string rule = "Kirmizi ve Mavi veya Mor ve Siyah";
// Kuralın denetleneceğin veri içeriği (Context)
List<string> myBasket =new List<string> { "Yesil", "Kahverengi", "Lacivert", "Sari", "Mor", "Siyah" };
RuleComputer computer = new RuleComputer();
// Kirmizi ve Mavi = 0 && 0 => 0
// 0 veya Mor = 0 || 1 => 1
// 1 ve Siyah = 1 && 1 => 1
bool result=computer.RunExpressionTree(rule,myBasket);
Console.WriteLine(result);
// Kirmizi ve Mavi = 0 && 0 => 0
// 0 veya Mor = 0 || 0 => 0
// 0 ve Siyah = 0 && 0 => 0
myBasket = new List<string> { "Yesil", "Kahve", "Lacivert", "Beyaz" };
Console.WriteLine(computer.RunExpressionTree(rule,myBasket));
// Kuralı değiştirelim
rule = "Kirmizi veya Beyaz";
// Kirmizi veya Beyaz = 0 || 1 => 1
Console.WriteLine(computer.RunExpressionTree(rule,myBasket));
// Exception testidir
// rule = "Sari";
// Console.WriteLine(computer.RunExpressionTree(rule, myBasket));
}
}
}</pre>
<p>Kodu dikkatlice incelemenizi öneririm.</p>
<p><img style="float: right;" src="/pics/2009%2f8%2fblg64_Scenario.jpg" alt="" />Tasarım kalıbımıza göre, <strong>AndExpression</strong> ve <strong>OrExpression</strong> tipleri kural içerisindeki <strong>ve, veya</strong> terimlerini ifade etmektedir. Diğer taraftan renklerin her birini <strong>ArgumentExpression</strong> tipi ile temsil ediyoruz. <strong>AndExpression</strong> ve <strong>OrExpression</strong> tipleri aynı zamanda kendi sol ve sağ taraflarındaki nesneleri kullanabilmek için <strong>RuleExpression</strong> tipinden referansları kullanıyorlar. Kodun belkide en önemli tipi <strong>RuleComputer</strong> sınıfı.</p>
<p>Tabir yerinde ise, <strong>Interpreter</strong> kalıbının önüne geçtiğini söyleyebiliriz. <strong>RuleComputer</strong> içerisinde yer alan <strong>RunExpressionTree</strong> metodu, ifade ağacının oluşturulması ve çalıştırılmasından sorumludur. Bu metodda kendi içerisinde <strong>Recursive </strong>olan başka bir fonksiyonu çağırmaktadır. Yazımızın başlarında hatırlayacağınız üzere örnek bir kuralı soldan sağa doğru yorumlayarak ele aldığımızı görmüştük. Burada kuralın n sayıda argüman ve operatörden oluşturulması söz konusu olduğundan, ifade ağacının çıkartılmasının tek yolu kendi kendini çağıran ve bir önceki çağırımda oluşturduğu ifadeyi kullanan bir metod yazmaktır.</p>
<p><strong>Main</strong> metodu içerisinde bir kaç test kuralı yazıldığını ve işletildiğini görmekteyiz. Kuralları işletiş şekline göre, <strong>ArgumentExpression</strong> tipine ait <strong>Interpret</strong> metodu içerisinde yaptğımız tek şey, parametre olarak gelen <strong>Context</strong><em>(yani renk bilgilerini içeren generic List koleksiyonu)</em> içerisinde, söz konusu referansın taşıdığı rengin olup olmadığına bakmak ve buna göre geriye <strong>true</strong> veya <strong>false</strong> sonuç döndürmektir.</p>
<p>Uygulamamızı <strong>debug</strong> ederek çalıştırdığımızda ise son derece güzel noktalara ulaştığımızı görebiliriz Söz gelimi ilk kuralın işletilmesi sırasında <strong>RuleComputer</strong> içerisindeki <strong>Expressions</strong> özelliğinin aşağıdaki yapıda olduğunu hemen farkedebiliriz.</p>
<p><img src="/pics/2009%2f8%2fblg64_QuickWatch.gif" alt="" /></p>
<p>Dikkat edileceği üzere <strong>string</strong> tabanlı yazılan basit kuralın her bir parçası <strong>Exrpression Tree</strong> üzerinde nesnel olarak yerini almış ve birbirlerine bağlanmıştır. Bundan sonrasında kodun yapması gereken tek şey, ağacı ilk elemandan sonuncuya kadar dolaşmak ve tüm gördüğü <strong>RuleExpression</strong> türevli tipler için <strong>Interpret</strong> metodlarını çağırmaktır. Ve işte çalışma zamanı sonucu;</p>
<p><img src="/pics/2009%2f8%2fblg64_Runtime.gif" alt="" /></p>
<p>Peki neler yapamıyoruz?</p>
<ul>
<li>Herşeyden önce sadece <strong>ve, veya</strong> operasyonlarına hizmet veren bir sistem söz konusu. Buna <strong>ancak</strong> operatörünüde ekleyebiliriz.</li>
<li>Diğer yandan, parantez yazımına destek verilmesi söz konusu olabilir. Bu duruma <strong>Expression Tree'</strong> nin oluşturulması sırasında parantez kullanımlarını değerlendirmemiz gerekecektir.</li>
<li>Kural olarak yazılan ifade bütününün, gerçekten doğru bir stilde yazıldığını denetlemek gerekir. Bitişik yazımlar yada tanımlı olmayan bir operatör(ve yerine yahu yazmış olabiliriz <img title="Wink" src="/editors/tiny_mce3/plugins/emotions/img/smiley-wink.gif" alt="Wink" border="0" /> ) hatalara neden olabilir.</li>
<li>...</li>
</ul>
<p>Maddeler elbetteki çoğaltılabilir. Ancak sonuçta ulaştığımız noktalardan birisi, belirli bir <strong>Context</strong> üzerinde, bizim belirlediğimiz bir kuralın işletilmesi ve sonuç olarak <strong>true</strong> yada <strong>false</strong> değere indirgenebilen bir çıktının ürettirilebilmesidir. Bir başka deyişle bu yapıyı esnetmek<em>(örneğin true/ false haricinde diğer tiplerin üretimine destek vermek yada =, != gibi çift taraflı karşılaştırma operasyonları hesaba katabilmek...)</em> tamamen klavyenin başındaki geliştiricini hayal gücü ile sınırlıdır. Tekrardan görüşünceye dek hepinize mutlu günler dilerim.</p>
<p><a href="https://www.buraksenyurt.com/pics/2009%2f8%2fInterpreterV2.rar">InterpreterV2.rar (26,55 kb)</a></p>2009-08-15T17:37:00+00:00design patternsoopc#bsenyurtBir süre önce tasarım kalıplarından Interpreter desenini incelemiş ve konu ile ilişkili bir kural motorunun çok basit anlamda nasıl yazılabileceğini araştıracağımızdan bahsetmiştik. Interpreter tasarım kalıbında hatırlayacağınız gibi Terminal ve NonTerminal tipleri bulunmaktadır...https://www.buraksenyurt.com/pingback.axdhttps://www.buraksenyurt.com/post.aspx?id=7d831647-1be5-449d-8899-39269889de6b0https://www.buraksenyurt.com/trackback.axd?id=7d831647-1be5-449d-8899-39269889de6bhttps://www.buraksenyurt.com/post/Tasarim-Kaliplari-Intpreter-ile-ikinci-randevu#commenthttps://www.buraksenyurt.com/syndication.axd?post=7d831647-1be5-449d-8899-39269889de6b