Sep 03
Refactoring : Table Driven Methods
Değerli arkadaşım Yahya ile ASP.MVC frameworkü pratik olarak öğrenmek için MyMovieCollection adında open-source bir proje geliştiriyoruz. Film koleksiyonu yapmayı sevdiğim için ancak böyle bir proje aklıma geldi.
Proje basit olarak kullanıcıların filmlerini saklamasını,düzenlemesini .. gibi işlemleri sağlıyor. Geçen gün projede yaptığım basit bir refactoring if-else, switch-case yapılarından kurtulmak için oldukça güzel bir yöntem olduğu için buraya da yazmak istedim.
Projede her filme kullanıcılar tarafından belirli bir puan verilebiliyor.Listeden seçilen elemana göre Filmlerde verilen puanın düştüğü belirli bir puan aralığına göre “İyi,Kötü,Çok iyi ” tarzı sınıflanıyorlar. Mesela 0-2 arası çok kötü 2-4 arası kötü gibi.Şimdi sınıfı aşağıya yazıp inceleyelim.
public class PointScale
{
public const string ALL = "Lütfen Seçiniz";
public const string VERYBAD = "Çok Kötü";
public const string BAD = "Kötü";
public const string NOTBAD = "Fena Sayılmaz";
public const string GOOD = "İyi";
public const string EXCELLENT = "Çok iyi";
private readonly double minPoint;
private readonly double maxPoint;
private readonly string scaleName;
private readonly int scaleID;
public PointScale(double minPoint, double maxPoint, string scaleName, int scaleID)
{
this.minPoint = minPoint;
this.maxPoint = maxPoint;
this.scaleName = scaleName;
this.scaleID = scaleID;
}
public static PointScale ParseScale(int scale)
{
switch (scale)
{
case 1:
return new PointScale(0, 1.99, VERYBAD, 1);
case 2:
return new PointScale(2, 3.99, BAD, 2);
case 3:
return new PointScale(4, 5.99, NOTBAD, 3);
case 4:
return new PointScale(6, 7.99, GOOD, 4);
case 5:
return new PointScale(8, 10, EXCELLENT, 5);
default:
return new PointScale(0, 10, ALL, 0);
}
}
public virtual double MinPoint
{
get { return minPoint; }
}
public virtual double MaxPoint
{
get { return maxPoint; }
}
public virtual string ScaleName
{
get { return scaleName; }
}
public virtual int ScaleID
{
get { return scaleID; }
}
}
Şimdi yukarıdaki sınıfın ParseScale metoduna bakmanızı istiyorum.Kullanıcı bu metodu çağırarak bir numara veriyor ve bu numaraya karşılık gelen PointScale nesnesi geri dönüyor. Gördüğünüz gibi metod içinde birçok switch-case yapısı mevcut.Şimdi bu switch-case yapılarından nasıl kurtulabiliriz biraz düşünün. Hemen akla gelmesede çok güzel ve basit bir yöntemle bu tarz koşullu yapılardan kolaylıkla kurtulabiliriz. Yeni halini yazalım ve incelemeye devam edelim.
public class PointScale
{
public const string ALL = "Lütfen Seçiniz";
public const string VERYBAD = "Çok Kötü";
public const string BAD = "Kötü";
public const string NOTBAD = "Fena Sayılmaz";
public const string GOOD = "İyi";
public const string EXCELLENT = "Çok iyi";
private static readonly IDictionary<int, PointScale> intToPointScale
= new Dictionary<int, PointScale>()
{
{0,new PointScale(0,10,ALL,0)},
{1,new PointScale(0, 1.99, VERYBAD, 1)},
{2,new PointScale(2, 3.99, BAD, 2)},
{3,new PointScale(4,5.99,NOTBAD,3)},
{4,new PointScale(6,7.99,GOOD,4)},
{5,new PointScale(8,10,EXCELLENT,5)}
};
private readonly double minPoint;
private readonly double maxPoint;
private readonly string scaleName;
private readonly int scaleID;
public PointScale(double minPoint, double maxPoint, string scaleName, int scaleID)
{
this.minPoint = minPoint;
this.maxPoint = maxPoint;
this.scaleName = scaleName;
this.scaleID = scaleID;
}
public static PointScale ParseScale(int scale)
{
if (intToPointScale.ContainsKey(scale))
return intToPointScale[scale];
return new PointScale(0, 10, ALL, 0);
}
public virtual double MinPoint
{
get { return minPoint; }
}
public virtual double MaxPoint
{
get { return maxPoint; }
}
public virtual string ScaleName
{
get { return scaleName; }
}
public virtual int ScaleID
{
get { return scaleID; }
}
}
Yukarıdaki ParseScale metoduna baktığımızda switch-case yapılarının ortadan kalktığını görüyoruz. Bunu basit olarak verilen numaraya karşılık gelecek olan PointScale nesnelerini tablo mantığı ile bir IDictionary intToPointScale nesnesinde eşleştirerek yapıyoruz. Ardından metod içinde verilen numaraya karşılık gelen nesne switch-case,if-else kontrolü yapmadan return intToPointScale[scale]; sayesinde geri dönülüyor. Bu şekilde uygulama oldukça esnek hale geldi. Dictionary içinde sakladığımız nesneleri istersek dışarıdan bir XML, yada Veritabanından alabiliriz ve herhangi bir if-else eklemek zorunda kalmayız. Bu tarz Tablo mantığı ile çalışan metodlara Table Driven Methods deniliyor. Çoğu durumda oldukça faydalı olabiliyor.

September 4th, 2008 at 2:51 am
Design Patterns adı altında 23 paterni inceleme imkanınız var mı?
Teşekkürler
September 4th, 2008 at 3:03 am
Aslında ileride belki 23 pattern hakkında yazmış olacağım.Fakat daha önceden yaptığım bir hata Pattern ezberlemekti bu yüzden genelde Design Pattern konuları ile alakalı ezbere kopyala-yapıştır mantığı ile yazmaktansa gerçek hayatta karşılaştığım problemlere çözüm sunan Pattern’lar hakkında yazmayı tercih ediyorum ve öğrenecek olanlara da bunu tavsiye ediyorum.Bu yüzden yavaş yavaş ortaya çıkması bence daha iyi zaten yeri geldikçe yazıyorum.Tasvsiyen için teşekkürler. Bizi izlemeye devam edin
September 10th, 2008 at 8:00 am
Okey sağolasın ben de Bridge Design Patterns için araştırma yapıyordum önerilerin olursa sevinirim.
September 29th, 2008 at 5:46 pm
[...] hala switch yapısı içerisinde.Bu tür veritabanından okunmayan statik değerler için Table Drven Methods [...]
October 25th, 2008 at 7:25 am
[...] hala switch yapısı içerisinde.Bu tür veritabanından okunmayan statik değerler için Table Driven Methods [...]
March 2nd, 2009 at 3:53 pm
[...] uygun olarak daha önceden bahsettiğim Table Driven Methods yöntemini kullanabiliriz. Bu şekilde if-else kontrollerinden kurtulup daha genel bir yapı [...]
March 13th, 2009 at 11:12 am
Webde dolaşırken gözüme çarptı. 2 kod parçası birbirinden biraz farklı bu yüzden dikkatli kullanmakta fayda var diye düşünüyorum. İkincisinde tüm oluşabilecek nesneleri ihtiyaç doğmadan önce toptan oluşturuyor ve ihtiyaç olduğunda bunları kullanıyor. İlkinde ise her ihtiyaç doğduğunda nesneyi tekrar tekrar oluşturuyor. Çok fazla kaynak gerektiren sınıflarda sorun olabilir.
March 13th, 2009 at 11:22 am
Gerçekten gerekmeden yapılan performans optimizasyonlarına(premature optimizastion) karşı olsamda yukarıdaki duruma performans olarak bakarsak da bence 2. yöntemdeki yani nesnelerin oluşturulup static dictionary içinde tutulması daha verimli çalışır. Çünkü uygulama bazında bu nesnelerde toplam 5 adet oluşturulacak. Fakat diğer durumda o metod her çağırıldığında (mesela uygulamada 1000 defa çağırıldı) 1000 adet nesne oluşturulacak. Tabi arada birinin static olup bellekte sürekli yer kaplaması diğerinin GC tarafından toplanması farkı var. Onunda problem olacağını düşünmüyorum.
May 4th, 2010 at 3:28 pm
[...] uygun olarak daha önceden bahsettiğim Table Driven Methods yöntemini kullanabiliriz. Bu şekilde if-else kontrollerinden kurtulup daha genel bir yapı [...]