Image
Ocak 04 2015 02:28

C# Factory Pattern

Altın kural :  sorun yoksa, design patternlere ihtiyaç ta yoktur.  Object orientedin bize verdiği yeteneklerle problemi çözebiliyorsak bu dizaynlara ihtiyacımız da yoktur.
İşte tam bu noktada “Artistik” olsun diye kod yazmanında alemi yok. :) (Jargon böyle benim suçum yok)
OOP un sunduğu altyapı yetersiz kalıyorsa mecburen problemimizi çözecek bir tasarımla işi halletmeye çalışacağız.

Burada en önemli mesele problemi doğru teşhis edebilmek. Peki bizi Factory Pattern kullanmaya sevk edecek nasıl bir problem olabilir? Bu arada adından da anlaşılacağı gibi pattern sınıf üretmek ile ilgili bir tasarım kalıbıdır.

özetle : Bir Abstract sınıftan türetilen alt sınıflar base class ın zorladığı bazı işleri, diğer altsınıflardan farklı şekillerde işlerler, işlemeliler…

Bir nesne üretmek istersem bu kod nerde olacaktır? elbette bir sınıfın içinde olacak.
– Peki bu sınıfın başka bir görevi var mı? (Single Responsibility e uyum)
– Nesne türetme şeklim değişebilir mi?
– Nesne üretme kriterim değişebilir mi ?
– Genişleme noktam neresi? bu noktada “Open Closed” prensibine uyuyor muyum?
bu sorulara “evet” cevabı veriyorsanız o create kodunu alıp bir factory sınıfına taşımanızı tavsiye ederim :d

senaryo :
Diyelim ki bir ürünümüz var bunu webte satmayı planlıyoruz ve ödemeleri  alacağımız bir sistemi nasıl kurarız diye düşünüyoruz.
Nasıl ödeme tipleri olur? Kredi kartı, havale, Eft vs ilk aklıma gelenler bunlar.
istenen : Client istediği ödeme tipini seçecek ona göre ilgili nesne oluşturulacak kendisine geri gönderilecek.

  • Türetme şeklim değişir mi ?
    Evet değişir, Kredi kartı kartı için başka şekilde, Havale için başka şekilde create etmemiz lazım.  Belki nesneyi oluştururken bir çok complex dataya ihtiyaç duyabilir, file pathe ihtiyacımız olabilir, parolalara şifrelere vs işlemleri olabilir.
  • Single Responsibility i bozar mı?
    Evet, yani sınıfa nesne yaratma işi ikinci bir görev yüklüyorsa biz onu dışarı çıkarmamız lazım.
  • Open Closed ilkesini – genişlerken değişmemeli
    Daha sonra başka ödeme tipleri de gelebilir paypal vs dolayısı ile her yeni ödeme tipi geldiğinde gidip eski yazdığımız kodları değiştirmemiz gerekir.

bu kadar konuştuktan sonra hadi kodlayalım.

Ödeme işlemi yapmamın amacı, bir şekilde müşterilerimden ödeme almak… Yani ile bütün ödeme tiplerinde OdemeAl() diye bir metodun olması lazım. Dolayısı ile bizim bunu bir Abstract sınıfta tanımlayıp  alt sınıfların bu metodu doldurmalarını zorunlu kılmamız lazım.

 

    internal class Musteri
    {
        public int Id { get; set; }
        public string Adi { get; set; }
    }

    interface IOdeme
    {
        void OdemeAl(Musteri musteri, int miktar);
    }

artık ekleyeceğimiz her ödeme tipini IOdeme  interfacesinden türeteceğiz.

class KrediKarti : IOdeme
    {
        public void OdemeAl(Musteri musteri, int miktar)
        {
            Console.WriteLine(string.Format("{0} müşterisinden {1} KrediKarti ile alındı", musteri.Adi, miktar));
        }
    }

    class Havale : IOdeme     {         public void OdemeAl(Musteri musteri, int miktar)         {             Console.WriteLine(string.Format("{0} müşterisinden {1} Havale ile alındı", musteri.Adi, miktar));         }     }     class Paypal : IOdeme     {         public void OdemeAl(Musteri musteri, int miktar)         {             Console.WriteLine(string.Format("{0} müşterisinden {1} Paypal ile alındı", musteri.Adi, miktar));         }     }

 

elbette ödeme işlemleri böyle kolay değil ama maksat pattern i anlatmak ??
ödeme işlemini alacak olan sınıflarımızda hazır olduktan sonra artık Clientin çağıracağı Factory clasını yazmamız gerekiyor.

 

static class PaymentFactory
    {
     internal   static IOdeme GetPayment(string odemeTipi)
        {
            switch (odemeTipi)
            {
                case "KrediKartı":
                    return new KrediKarti();

                case "Havale":                     return new Havale();                 default:                     return new Paypal();             }         }     }

çıktısı :

Blog image


tamam böylede çalışıyor ama bir terslik var .

Genişlediğim zaman kodda değişiklik yapmamam gerekiyordu ben her ödeme tipi eklendiğinde gelip bu factory class ını değiştirmek istemiyorum.

O zamanda .NET platformun sağladığı reflection olayından faydalanıp factory clasına gelen string değerden nesnenin tipini öğrenip o tipte bir nesne create edelim.

static class PaymentFactory
    {
     internal   static IOdeme GetPayment(string odemeTipi)
        {
            Type readerType = Type.GetType(odemeTipi);
            return Activator.CreateInstance(readerType) as IOdeme;
        }
    }

ve elbette factoryi çağırdığımız yere artık ödeme tiplerine ait classların namespace leri ile birlikte isimlerini vermek gerekiyor.

 

 IOdeme o1 = PaymentFactory.GetPayment("ConsoleApplication1.KrediKarti");
  o1.OdemeAl(musteri,500);
  IOdeme o2 = PaymentFactory.GetPayment("ConsoleApplication1.Havale");
  o2.OdemeAl(musteri, 1500);
  IOdeme o3 = PaymentFactory.GetPayment("ConsoleApplication1.Paypal");
  o3.OdemeAl(musteri, 2500);

 

2015 : memet tayanç