Generic Factory

11 Aralık
2008

Factory Method Pattern yaratım türleri içerisinde naçizane bir yere sahip müstesna bir class yaratım yoludur. Şöyle ki bazen bazı class ların üretimi ile ilgili bir başka class’ı sorumlu tutmak isteyebilirsiniz. Mesela bir class a ait event leri belli bir merkezde toplamak onların raise ettikleri eventleri bir noktadan görmek isteyebilirsiniz. Diyelim ki birkaç adet farklı işi yapan class’ınız mevcut, bu class ların yaptıkları işlerden sonra bir loglama merkezine işin bittiği sıradaki diğer işe geçildiği ile ilgili bilgi vermeleri gerekiyor. “Sub New” yaratımı ile bu işlemi yapıp sonra bu iş bitimi ile ilgili event’i bir shared metoda bağlamanız mümkün, ya da iş bittikçe shared bir metodu çağırmanız ama olabildiğince az modikasyonla en fazla genişlemeyi sağlayabilmek için (kısaca OCP) factory method pattern’ı bu gibi yerlerde kullanmamız gerekli olmaktadır.

Genel olarak “Factory Method” dediğimiz belli bir tür class ı yaratan ve döndüren bir fonksiyondan başkası değildir. Bir örnek kodla inceleyelim;

Public Class Fabrika
    Shared Function Yarat(ByVal isim As String) As Hayvan
        Dim h As New Hayvan
        h.Init(isim)
        Return h
    End Function
End Class

<span id="more-76"></span>

Public Class Hayvan
    Sub Init(ByVal isim As String)
        _isim = isim
    End Sub

    Private _isim As String
    Public ReadOnly Property İsim() As String
        Get
            Return _isim
        End Get
    End Property
End Class

Public Module MainApp
    Sub New()
        Dim kedi As Hayvan = Fabrika.Yarat(&quot;Tom&quot;)
        Dim fare As Hayvan = Fabrika.Yarat(&quot;Jerry&quot;)
        Console.WriteLine(kedi.İsim)
        Console.WriteLine(fare.İsim)
        Do
            Console.ReadLine()
            Exit Do
        Loop
    End Sub
End Module

Bu kodda görebileceğimiz üzere bir adet “hayvan” isimli class ımız ve o class a ait “İsim” özelliğimiz var. Shared olarak tanımlanmış “Yarat” metodumuz bize yeni bir hayvan yaratıp çıktı olarak gönderiyor. Bu örnekte mevcut değil ama mesela daha önceden aynı isimle yaratılmış bir class ı yeniden istediğimizde o obje yi yaratmadan var olanını döndürmeyi bu fabrika sayesinde sağlayabilirdik. Bu da yukarıda anlattığım “Factory Method Pattern” güzelliklerinden bir diğeri oluyor. “Object Reusability” diyebileceğimiz naçizane konu. Bu tip class ların kullanıldığı büyükçe bir kodlama düşünürseniz her hayvan class ını kullanacak olan farklı farklı kod rutinlerinde objeleri yeniden yeniden yarattırmak yerine yeniden kullanılabilirlilik adına “Factory Method Pattern” işimizi fazlasıyla kolaylaştırılıyor.

Neyse işin özü bu aslında ama burda göze çarpabilecek ve şu haliyle fabrikanın başaramadığı bir konu var. Mesela benim Hayvan class ından başka tipte, ya da hayvan class ından türemiş başka başka class larım var. Ve tüm bu türleri tek fabrikadan ürettirmem gerekli ise diyelim. Hepsi için ayrı ayrı birer fabrika yazabilirim mümkün ama 20 tane ayrı class varsa işin içinde nasıl bir kod olur o, nerde kalır OCP vs… O yüzden şimdi “generic factory” isimli bir fabrika yazacağım. Önce kod;

Public Class GenericFabrika
    Shared Function Yarat(Of T)(ByVal isim As String) As T
        If GetType(T).BaseType Is GetType(HayvanBase) Then
            Dim obj As Object = Activator.CreateInstance(GetType(T))
            Dim h As HayvanBase = DirectCast(obj, HayvanBase)
            h.Init(isim)
            Return obj
        Else
            Throw New Exception("Sadece HayvanBase sınıfından türeyenler bu fabrikada üretilebilir.")
        End If
    End Function
End Class

Public MustInherit Class HayvanBase
    Sub Init(ByVal isim As String)
        _isim = isim
    End Sub

    Private _isim As String
    Public ReadOnly Property İsim() As String
        Get
            Return _isim
        End Get
    End Property

    Overridable Function Tür() As String
        Return "Genel bir hayvan"
    End Function

    MustOverride ReadOnly Property Renk() As String
End Class

Public Class Kedi
    Inherits HayvanBase

    Sub Miyavla()
        Console.WriteLine("Miyavvv")
    End Sub

    Public Overrides Function Tür() As String
        Return "Kedi"
    End Function

    Public Overrides ReadOnly Property Renk() As String
        Get
            Return "siyah"
        End Get
    End Property
End Class

Public Class Fare
    Inherits HayvanBase

    Public Overrides Function Tür() As String
        Return "Fare"
    End Function

    Public Overrides ReadOnly Property Renk() As String
        Get
            Return "kahverengi"
        End Get
    End Property
End Class

Public Module GenericMainApp
    Sub New()
        Dim _kedi As Kedi = GenericFabrika.Yarat(Of Kedi)("Tom")
        Dim _fare As Fare = GenericFabrika.Yarat(Of Fare)("Jerry")

        Dim hayvanlar() As HayvanBase = {_kedi, _fare}
        For Each h As HayvanBase In hayvanlar
            Console.WriteLine(h.İsim &amp; " isimli hayvan " &amp; h.Renk &amp; " renginde")
        Next

        _kedi.Miyavla()

        Do
            Console.ReadLine()
            Exit Do
        Loop
    End Sub
End Module

Bu sefer fabrikadaki yarat fonksiyonumuzu generic olarak tanımladık. Bu yeni fonksiyonda ilk başta istenilen T türündeki çıktının aslında HayvanBase class ından türemiş olup olmadığına bakıyoruz. Programın durumuna göre gerekli olmayabilir ama böyle bir doğrulama ile koda başlamak mantıklı. Sonra işin sihir kısmı devreye giriyor “Activator” nesnesi: Burda görülebileceği üzere CreateInstance fonksiyonu da bir fabrika sayılabilir. Ama farklı bir şekilde generic tanımlanmış. Activator ile gerekli class ı yarattıktan sonra bu object class ı hayvanbase’e cast edip ortak olarak tanımlanmış “Init” metodunu çağırıyoruz.

Burada dikkat edilmesi gereken bir diğer nokta “HayvanBase” den türeyen nesnelerdeki özgürlüktür. Eskiden tek tür ile çalışırken hayvanın türüne göre bir eklenti yaptığımızda tüm hayvanlar için geçerli olması gerekirdi ama burada inheritance’ı tam anlamıyla kullanıyoruz. Ortak değerleri (mesela isim) tüm class larda ortak olacak şekilde base de tanımlarken hayvanlara ait özel bir metodu da ayrıca ve sadece o hayvana özgü biçimde tanımlayabiliyoruz (mesela miyavla). Ek olarak türeyen class lara bazı gerekli metodları tanımlaması için zorlayabiliyoruz (mesela Renk). Bunlar generic factory ile alakadar olmayan şeyler tabi hepsi inheritance ın güzellikleri ancak bu şekilde tanımlayınca fabrikamızı hem OCP ye daha bir uygun kod ortaya çıkıyor hem de inheritance ı fazlasıyla kullanıyoruz…

Yorumlar kapalı.

başa dön