22 Kasım 2012 Perşembe

Dependency Injection Tasarım Deseni


Yazılım dünyasında ürünlerden daha çok prensiplere ve ilkelere önem veririm. Java veya C# dilinde yazmışım çokta dert değil benim için. Bir dilin yazım kurallarını ve ürün geliştirme araçlarını öğrenmek çokta uzun zaman almaz. Yazılım geliştirdiğimiz ortamların (IDE) pratiklerini anlamak çok önemlidir tabikide. Ama yazılım prensipleri ve ilkelerini benimsemek, o ilkelere sadık kalarak yazılım geliştirmek bana göre daha önemlidir.
Resimde ne görüyorsunuz diye bir soru sorsam, süpheli süpheli bakmaya mı başlarsınız, yoksa USB anahtarlıklar ve iki anahtar mı dersiniz bilmiyorum. Ama sistematik bir görüntü var aslında resimde. Canı isteyen sistemdeki anahtarlıkları halkadan boşandırıp değiştirebilir. Çünkü halka esnek bir yapıda. Aynı şekilde isteyen, ipliğin ucundaki USB aygıtlarını da istediği renkle değiştirebilir. Çünkü iplikte basit bir düğüm, dolayısıyla anahtarlık ve anahtarlar arasında gevşek bir bağ mevcut. Aynı şekilde USB aygıtlarının kapakları da birbirine uyumlu olduklarından  istenen renkte seçilebilir. Herşey esnek, herşey gevşek bağlı.
Yazılım tasarım desenlerinde de sıklıkla karşılaştığım “gevşek bağlılık” deyiminin de ifade etmek istediği şey yukarıdaki şekilde anlatıldığı gibidir. Nesnelerin birbirine uyumlu ve birbirini etkilemeyecek şeklide olmasıdır.
Yazılım tasarım desenlerinden biri olan, Dependency Injection (Bağımlılıkların sağlanması) prensibi de bileşenlerin(components) birbirine olan sıkı bağlarının gevşetilmesi ve yazılımın test edilebilir bir hale getirilmesini amaçlamaktadır. Test edilebilir tasarım konusuna daha sonraki yazılarda değinmek istiyorum. Şu anda ilgileneceğimiz tasarımda ki gevşek bağlılık olacaktır.
Bu durumu bir örnek uygulama üzerinden anlatmak daha uygun  olacak. Senaryomuz bir şirketin çalışanlarının aylık ve günlük satış istatistiklerini veren bir sınıfın tasarlanması şeklinde olacaktır.
public interface IEmployeeStatistics
{
       decimal GetDailySales(int employeeId);
       decimal GetMonthlySales(int employeeId);
}
Yukarıdaki arayüz(interface), diğer bir deyişle sözleşme, employeeId bilgisi verilen çalışanın günlük satış tutarını verecek şekilde hazırlanmıştır.
Aşağıdaki EmployeeStatistics sınıfı ise IEmployeeStatistics arayüzünden türetilmiş sınıfların getireceği istatistikleri dış dünyaya sunmaktan sorumlu olacaktır.
public class EmployeeStatistics
{
       private IEmployeeStatistics employeeStatistics;
       public EmployeeStatistics(IEmployeeStatistics employeeStatistics)
       {
              this.employeeStatistics = employeeStatistics;
       }
       public decimal GiveMeDailyReports(int employeeId)
       {
              return employeeStatistics.GetDailySales(employeeId);
       }
       public decimal GiveMeMonthlyReports(int employeeId)
       {
              return employeeStatistics.GetMonthlySales(employeeId);
       }
}
EmployeeStatistics sınıfının kurucu metodundaki bağımlılık, IEmployeeStatistics arayüzü tarafından sağlanmıtır.
Dış dünyaya sunum yapan sınıf olan EmployeeStatistics sınıfı, eline gelen istatistiklerin hangi veritabanından kullandığını, hangi aşamalardan geçtiğini, ne zorluklar çektiğini bilmez. Hatta sınıfın adını bile bilmez. EmployeeStatistics sınıfı için önemli olan sadece constructor metoduna gelecek olan tipin IEmploeyeeStatistics arayüzünü uygulamış (implement) olmasıdır. Aynen yukardaki resimde anahtarlığa bağlanan USB anahtarlıklarının ne şekilde olduğunun önemli omadığı gibi. İpliğin girebileceği uygun bir yer olsun yeter.
Şimdi de MSSQL veritabanından istatistik çeken sınıfı oluşturalım.
public class EmoleeStatisticsFromMSSQL : IEmployeeStatistics
{
       public decimal GetDailySales(int employeeId)
       {
              //.....
              //.....
              // MSSQ'den getirme işlemleri
              decimal result = 5214M;
              return result;
       }
       public decimal GetMonthlySales(int employeeId)
       {
              //.....
              //.....
              // MSSQ'den getirme işlemleri
              decimal result = 23514M;
              return result;
        }
}
Eğer EmployeeStatistics sınıfı IEmployeeStatistics tipini değilde şu anda oluşturduğumuzEmployeeStatisticsFromMSSQL tipini direk kullansaydı iki sınıf arasında (EmployeeStatistics ve EmployeeStatisticsFromMSSQL) sıkı bir bağ oluşacaktı. Bu durumda, sistemde bir connection sorunu olduğunda veya veri katmanında farklı bir sorun oluştuğunda,EmployeeStatistics sınıfında değişiklik yapılacaktı. Ancak bu sıkı bağı IEmployeeStatisticsarayüzü ortadan kaldırdı ve esnek bir bağ oluşturdu.
Bir süre sonra şirketiniz veritabanı yazılımını MSSQL yerine Oracle tarafına geçirmek istediğinde yapmamız gereken sadece EmployeeStatisticsFromOraclesınıfını oluşturmak olacaktır
public class EmoleeStatisticsFromOracle : IEmployeeStatistics
 {
       public decimal GetDailySales(int employeeId)
       {
              //.....
              //.....
              // Oracle'dan getirme işlemleri
              decimal result = 5214M;
              return result;
       }
       public decimal GetMonthlySales(int employeeId)
       {
               //.....
               //.....
               // Oracle'dan getirme işlemleri
               decimal result = 23514M;
               return result;
       }
}
Bu sayede yazılımımız değiştirilmeye kapalı , geliştirilmeye açık hale gelmiş olacaktır. Yani yazılım prensiplerinden biri olan Open Closed (Gelişime açık değişime kapalı) prensibine de uygun hale gelmiştir.
Sınıfın kullanımı ise şu şekilde olacaktır.
public class Run
{
       public void Raporla()
       {
             IEmployeeStatistics reportProvider = new EmoleeStatisticsFromMSSQL();
             EmployeeStatistics statistics = new EmployeeStatistics(reportProvider);
             var daily = statistics.GiveMeDailyReports(1);
             var monthly = statistics.GiveMeMonthlyReports(1);
       }
}
Kullanıma hazır bir yapıyı oluşturabildik. Tekrar görüşmek ümidiyle.





Kaynak : http://www.bayramucuncu.com/dependency-injection-tasarim-deseni/

Hiç yorum yok: