Fluent Interface kavramı 20 Aralık 2005 tarihli
Fluent Interface (akıcı arayüz) başlıklı makalesinde anlattığı gibi Martin Fowler ve Eric Evans tarafından bulunmuştur.
Temel
olarak birbiri arkasına aynı satırda çağrılan methodlar ile okunurken
yaptığı işi anlatan methodlar hazırlanmasıdır. Bunu yapmak için bir
methoddan geriye aynı nesneyi dönmek yeterlidir.
Method
çağrılarının "akıcı" şekilde okunması ile yapılan işin kendi kendini
anlatır şekilde, dokumente edilmiş, yorum satırı ile desteklenmiş gibi
açıklayıcı
Örneğin Field bazında sorgulama yapmak için kullanılacak bir sınıf tanımlayalım ve bunun Fluent Interface
public class FieldCriteria
{
public string FieldName { get; private set; }
public FieldCriteria(string fieldName)
{
this.FieldName = fieldName;
}
public FieldCriteria StartsWith(string startsWith)
{
return this;
}
public FieldCriteria EndsWith(string endsWith)
{
return this;
}
public FieldCriteria Contains(string contains)
{
return this;
}
public FieldCriteria LengthOf(int length)
{
return this;
}
}
Yukarıdaki
sınıfta StartsWith, EndsWith,Contains,LengthOf methodlarının içeride
kendi işlerini yaptığını ve geriye yine aynı nesneyi (yani this)
döndüğünü görüyorsunuz. Bu şekilde akıcı bir method çağrısı
oluşturulmuş olur.
FieldCriteria sınıfından bir instance aldığımızda onu aşağıdaki şekilde kullanabiliriz;
FieldCriteria c = new FieldCriteria("Name").LengthOf(5).StartsWith("C").EndsWith("Z").Contains("N");
veya bana göre daha okunuşlu şekilde
FieldCriteria c = new FieldCriteria("Name")
.LengthOf(5)
.StartsWith("C")
.EndsWith("Z")
.Contains("N");
Method
çağrı zincirini (ingilizce kanykalarda method chaining olarak
bulabilirsiniz) ingilizce olarak okumaya çalıştınızda uzunluğu 5 olan C
ile başlayan Z ile biten ve N içeren bir Name alanı kriteri
oluşturduğumuz gayet açık gözüküyor sanırım.
Konu ile ilgili
araştırma yaparken Ruby'de olan bir özellik ile ilgili kaynaklara
ulaştım. Aşağıdaki gibi bir çağrı ile 10dk. önces/sonrası için DateTime
nesnesine erişebiliyorsunuz. Bunu C# ile uygulamak için Extension
Method kullanmamız gerekmektedir.
10.minutes().ago()
10.minutes().later()
gibi bir erişim için öncelikle int veri türünü bir method eklememiz gerekiyor.
public
static class DateTimeExtensionMethods
{
public
static TimeSpan minutes(
this int mins)
{
return new TimeSpan(0, mins, 0);
}
public
static DateTime
ago(
this TimeSpan ts)
{
return DateTime.Now.Add(-ts);
}
public
static DateTime
later(
this TimeSpan ts)
{
return DateTime.Now.Add(ts);
}
}
Yukarıdaki kod int veri türüne minutes şeklinde bir method ekler ve bu method geriye TimeSpan döner.
TimeSpan nesnesine ise ago ve later adında iki method ekler.
Bu
methodlarda geriye DateTime.Now değerini baz alarak genişletilmiş
TimeSpan sınıfın o anki örneğinin değerini ekleyerek/çıkararak yeni bir
DateTime nesnesi döner.
Console.WriteLine(10.minutes().ago().ToString());
Console.WriteLine(10.minutes().later().ToString());
Console.ReadLine();
Yukarıda ki kod bloğu çalıştırıldığında çıktısı aşağıda ki gibi olacaktır.
12.08.2009 17:20:25
12.08.2009 17:40:25
Fluent
Interface yöntemi yazılım geliştirirken sizlere bir bakış açısı
katacaktır ancak Fluent Interface yöntemi diğer tüm kalıplar gibi her
projede kullanılmaz. Genel olarak yeniden kullanılabilir kütüphanelerde
yapılandırma amacıyla (NHibernate konfigurasyonu bu şekilde
yapılabiliyor) ya da ilk örnekte verdiğim kriter örneği gibi durumlarda
kullanılabilir. Fluent Interface olmadan olmaz değil tabi ki ancak
uygun yerlerde kullanıldığında kod kalitesini ve okunabilirliğini
artıracağınız bir gerçektir.