Composition Over Inheritance (Kalıtım yerine Birleşim)

Uzun zamandır işlerin yoğunluğundan dolayı yazamıyordum ama telafisi olarak düşündüğüm Object Oriented programlama açısından hayati konulara değinerek kendimi affettireceğimi umuyorum :)

Şimdi neden önemlidir ondan biraz bahsedelim. Öncelikle Object Oriented programlama dillerinde kodun tekrar kullanımını(code reuse) iki şekilde sağlayabilirsiniz. Birincisi inheritance(kalıtım) ikincisi ise Composition(güzel bir türkçe isim bulana kadar “birleşim” :) ) yöntemidir. Şimdi bu iki kullanımın neler olduğuna bakalım. Önce UML sınıf diyagramı olarak aralarındaki farka bakalım.

diyagram

Birde aşağıdaki sınıfların kod olarak aralarındaki farklarına bakalım inheritance(kalıtım) kullanan versiyonu :

public class TemelSinif
{
    public int ozellik1;
    public int ozellik2;

    public void metodA()
    {
        //metodA icerigi
    }

    public void metodB()
    {
        //metodB icerigi
    }
}

public class Sinif : TemelSinif
{
    public int farkliOzellik;

    public void farkliMetod()
    {
        //TemelSinif icindeki metodu kullanan ya da
        //kullanmayan herhangi bir metod
    }
}

Composition kullanan versiyonu :

public class BaskaSinif
{
    public int ozellik1;
    public int ozellik2;

    public void metodA()
    {
        //metodA icerigi
    }

    public void metodB()
    {
        //metodB icerigi
    }
}

public class Sinif
{
    public int ozellik;
    public BaskaSinif myBaskaSinif;

    public void metod()
    {
        //BaskaSinif icindeki metodu kullanan ya da
        //kullanmayan herhangi bir metod
    }
}

Yukarıdaki diyagramdan ve koddan inheritance ve composition arasındaki farkı anlamış olduğunuzu umuyorum. Inheritance’da temel bir sınıftan türeyerek o sınıfın özelliklerini alıp kullanabiliyor, Composition’da ise özelliğini kullanmak istediği sınıfın referansını içinde tutuyor ve gerektiğinde o sınıfın metodlarını çağırarak(delegation) diğer sınıfın özelliklerini ve metodlarını kullanabiliyor.

Genellikle Object Oriented programlamaya yeni başlayanların(bende dahil olmak üzere) kodu tekrar kullanmak için en çok kullandıkları teknik Inheritance oluyor.Bunda bizimde çok fazla suçumuz yok açıkçası okullarda,kurslarda,birçok yerde object oriented gelince malesef ilk olarak akla inheritance geliyor ve genel örnekler Kedi ve Köpek sınıflarını Hayvan sınıfından türetmekten ileriye gidemiyor. Hal böyle olunca bizde inheritance çok güzel birşey deyip heryerde kullanmaya başlıyoruz ve problemler ortaya çıkmaya başlıyor. Bu problemler nelerdir ve Inheritance yerine çoğu durumda neden Composition tekniği kullanmamız daha faydalı olur incelemeye başlayalım.

İşe herzaman olduğu gibi acizane örneğimiz ile başlayalım. Bu arada orjinal bir örnek bulana kadar baya zorlandım fakat her türlü kötü kod örneğini barındıran eski yazdığım kodlar içerisine bakınca konu için sonunda güzel bir örnek bulabildim :). Bu kodu daha önceden,.NET 1.0 zamanlarında staj yaptığım yerde küçük çaplı veritabanı uygulamalarında kullanmak için yazmıştım.Ne kadar kötü olduğuna bakmayın, ya da gerçek bir projede buna benzer şeyler yapmaya sakın kalkışmayın :) Bundan çok daha fazlasını yapan Java ve .NET için birçok araç mevcut(NHibernate,Subsonic,Ado Entity,Framework,JDO,Hibernate…)

Neyne uzatmadan konumuza dönelim. Yaptığım şey veritabanına kayıt etmek,silmek,güncellemek istediğim nesnelerin INSERT,UPDATE,DELETE gibi SQL cümlelerini yazmaktan kurtulmak ve otomatik olarak üretilmesini sağlamaktı. Bunu yapmak için Reflection kullanıyordum. Bu arada şuanda yapıya baktığımda kendi çapımda ActiveRecord pattern uygulayan küçük bir ORM yapmaya çalışıyormuşum tabi o zamanlar bunlardan pek haberim yoktu belki olsaydı NHibernate yerine türk stili bir Object Relational Mapping aracımız bile olabilirdi :)

public abstract class DataObject
{
    protected DataLayer dataLayer = new DataLayer();

    public virtual string Kaydol(string TableName, object sender)
    {
        Type tip = sender.GetType();

        System.Reflection.PropertyInfo[] Ozellik = tip.GetProperties(BindingFlags.Public | BindingFlags.Instance);

        string strSQL = "INSERT INTO " + TableName + " (";
        string deger = "";
        for (int i = 0; i < Ozellik.Length; i++)
        {
            deger += Ozellik[i].Name.ToString() + ",";
        }

        deger = deger.Remove(deger.LastIndexOf(","), 1) + ") VALUES(";

        for (int i = 0; i < Ozellik.Length; i++)
        {
            try
            {
                if (Ozellik[i].GetValue(sender, null) != null)
                    deger += "'" + Ozellik[i].GetValue(sender, null).ToString() + "'" + ",";
                else
                    deger += "'',";
            }
            catch
            {
            }
        }

        deger = deger.Remove(deger.LastIndexOf(","), 1) + ")";

        strSQL += deger;
        DataLayer.ExecuteSQL(strSQL);

        return strSQL;
    }

    protected virtual string Guncelle(string TableName, object sender, int GuncellenecekID, string IDAlanAdi)
    {
        string strSQL = "UPDATE " + TableName + " SET ";
        string deger = "";

        Type tip = sender.GetType();
        PropertyInfo[] Ozellikler = tip.GetProperties(BindingFlags.Public | BindingFlags.Instance);

        for (int i = 0; i < Ozellikler.Length; i++)
        {
            if (Ozellikler[i].GetValue(sender, null) != null && Ozellikler[i].GetValue(sender, null).ToString() != "0")
                deger += Ozellikler[i].Name + "='" + Ozellikler[i].GetValue(sender, null) + "',";
        }

        deger = deger.Remove(deger.LastIndexOf(","), 1);

        string strWhere = " WHERE " + IDAlanAdi + "=" + GuncellenecekID + "";

        strSQL += deger + strWhere;

        DataLayer.ExecuteSQL(strSQL);
        return strSQL;
    }

    protected virtual void KayitGetir(string TableName, object sender, int BulunacakID, string IDAlanAdi)
    {
        string strSQL = "SELECT * FROM " + TableName + " WHERE " + IDAlanAdi + "=" + BulunacakID + "";

        DataSet ds = dataLayer.GetDataTable(strSQL);

        Type tip = sender.GetType();
        PropertyInfo[] Ozellikler = tip.GetProperties(BindingFlags.Public | BindingFlags.Instance);

        for (int i = 0; i < Ozellikler.Length; i++)
        {
            for (int j = 0; j < ds.Tables[0].Columns.Count; j++)
            {
                if (Ozellikler[i].Name == ds.Tables[0].Columns[j].ColumnName.ToString())
                {
                    if (ds.Tables[0].Rows[0][j] != null && ds.Tables[0].Rows[0][j].ToString() != "")
                        Ozellikler[i].SetValue(sender, ds.Tables[0].Rows[0][j], null);
                }
            }
        }

        ds.Dispose();
    }
}
public class Haber : DataObject
{
    private int m_HaberID;
    private string m_Metin;

    public int HaberID
    {
        get { return m_HaberID; }
        set { m_HaberID = value; }
    }

    public string Metin
    {
        get { return m_Metin; }
        set { m_Metin = value; }
    }

    public void KayitGetir(string TableName, int ID)
    {
        base.KayitGetir(TableName, this, ID, "HaberID");
    }

    public void Guncelle(string TableName, int ID)
    {
        base.Guncelle(TableName, this, ID, "HaberID");
    }

    public int BugununHaberSayisi()
    {
        DataTable dt = dataLayer.GetDataTable("SELECT COUNT(*) FROM Haber WHERE Tarih=GetDate()").Tables[0];
        return Convert.ToInt16(dt.Rows[0][0].ToString());
    }
}

Örneğimizde kodun tekrar kullanımı için SQL cümlesi oluşturan metodları DataObject sınıfında toplamışız ve bu kodu tekrar kullanmak için diğer sınıfları Inheritance kullanarak bu sınıftan türetiyoruz. Yukarıda gördüğünüz Haber sınıfı DataObject sınıfından türüyor ve kendi referansını temel sınıfa göndererek veritabanı işlemlerini gerçekleştirmiş oluyor.Tabi Haber sınıfının yanında onlarca sınıfda DataObject sınıfından türüyor. UML diyagramına bakalım:

Yukarıdaki kod şuana kadar problemsiz çalışıyor. Fakat bazı dezavantajları var. En önemlisi Inheritance temel sınıf(superclass,baseclass) ile ondan türeyen sınıfları(subclass) birbirine sıkı şekilde bağımlı hale getiriyor. Yani DataObject içinde yaptığınız herhangi bir değişiklik alt sınıfları etkileme potansiyeline sahip. Mesela Haber sınıfı içerisindeki BugununHaberSayisi metodu DataObject sınıfında protected olan dataLayer nesnesini kullanarak özel bir sorgu çalıştırıp haber sayısını buluyor. DataObject sınıfında bu alanı değiştirdiğimizde otomatik olarak Haber sınıfı gibi ondan türeyen alt sınıflar bundan etkilenecek bu yüzden temel sınıflarda birşeyi değiştirirken çok dikkatli olmamız ve ondan türeyen sınıfların hangi özelliklerini kullandığına bakmamız gerekiyor.

Bu kadar riskle başa çıkabiliriz diyelim ve devam edelim. Bizim güzel sınıflarımız çalışmaya devam ederken aklımıza programımızda neler olup bittiğini görmek için log tuttuğumuz geliyor. Log dosyalarında da açıklayıcı olması için nesnelerin bütün alanlarını ve değerlerini log dosyası içine yazdırmamızın hata ayıklamada işimizi daha da kolaylaştıracağını farkediyoruz.Çünkü daha log dosyalarında anlamlı veri aramak işimizi baya zorlaştırıyordu.Yani bizim için nesnenin bütün özellikleri ve değerini yazan bir çeşit ToString metodumuzun olmasını istiyoruz. Normal ToString metodu bildiğiniz gibi Object sınıfına ait ve override etmediğiniz sürece nesnenin sınıf adı tarzı pek işimize yaramayan özelliklerini ekrana yazdırıyor.

Buraya kadar herşey güzel nesnenin özelliklerini ve değerini yazan metodumuzu Reflection kullanarak yazdık.Şimdi zurnanın zort dediği yere gelmiş bulunuyoruz :) Bu metodu nereye koymalıyız ? Eğer başka bir sınıf içerisine koyarsak Bizim Haber gibi DataObject sınıfından türeyen nesnelerimiz onu Inheritance ile kullanamayacaklar çünkü C#,Java gibi diller nesneler için çoklu kalıtıma izin vermiyorlar.( Şekil A da görüldüğü gibi..)

class StringBase
{
    public override string ToString()
    {
        StringBuilder toString = new StringBuilder();
        PropertyInfo[] props = GetType().GetProperties();

        foreach (PropertyInfo prop in props)
        {
            toString.AppendFormat("{0} : {1} || ", prop.Name, prop.GetValue(this, null));
        }

        return toString.ToString();
    }
}
public class Haber : DataObject, StringBase
{
    private int m_HaberID;
    private string m_Metin;

    public int HaberID
    {
        get { return m_HaberID; }
        set { m_HaberID = value; }
    }

    public string Metin
    {
        get { return m_Metin; }
        set { m_Metin = value; }
    }

    public void KayitGetir(string TableName, int ID)
    {
        base.KayitGetir(TableName, this, ID, "HaberID");
    }

    public void Guncelle(string TableName, int ID)
    {
        base.Guncelle(TableName, this, ID, "HaberID");
    }

    public int BugununHaberSayisi()
    {
        DataTable dt = dataLayer.GetDataTable("SELECT COUNT(*) FROM Haber WHERE Tarih=GetDate()").Tables[0];
        return Convert.ToInt16(dt.Rows[0][0].ToString());
    }
}

Yukarıdaki kod pek işe yaramadı ama bizi yıldıramaz.”Aptal bilgisayar kodumu tekrar kullanmamı önleyemezsin” deyip başka bir çözüm aramaya koyuluyoruz. Ve aklımıza neden bunu DataObject sınıfına koymadığımız geliyor.Sonuçta Haber,Makale… gibi nesnelerimiz bu nesneden türüyor dolayısıyla ona koyduğumuz metod diğer metodlar gibi alt sınıfları tarafından kullanılabilecekler.Reflection ile çalışan ToString metodunu DataObject sınıfımıza koyup tekrar aşağıdaki gibi kodumuzu düzenliyoruz.

public abstract class DataObject
{
    protected DataLayer dataLayer = new DataLayer();

    public virtual string Kaydol(string TableName, object sender)
    {
        Type tip = sender.GetType();

        System.Reflection.PropertyInfo[] Ozellik = tip.GetProperties(BindingFlags.Public | BindingFlags.Instance);

        string strSQL = "INSERT INTO " + TableName + " (";
        string deger = "";
        for (int i = 0; i < Ozellik.Length; i++)
        {
            deger += Ozellik[i].Name.ToString() + ",";
        }

        deger = deger.Remove(deger.LastIndexOf(","), 1) + ") VALUES(";

        for (int i = 0; i < Ozellik.Length; i++)
        {
            try
            {
                if (Ozellik[i].GetValue(sender, null) != null)
                    deger += "'" + Ozellik[i].GetValue(sender, null).ToString() + "'" + ",";
                else
                    deger += "'',";
            }
            catch
            {
            }
        }

        deger = deger.Remove(deger.LastIndexOf(","), 1) + ")";

        strSQL += deger;
        DataLayer.ExecuteSQL(strSQL);

        return strSQL;
    }

    protected virtual string Guncelle(string TableName, object sender, int GuncellenecekID, string IDAlanAdi)
    {
        string strSQL = "UPDATE " + TableName + " SET ";
        string deger = "";

        Type tip = sender.GetType();
        PropertyInfo[] Ozellikler = tip.GetProperties(BindingFlags.Public | BindingFlags.Instance);

        for (int i = 0; i < Ozellikler.Length; i++)
        {
            if (Ozellikler[i].GetValue(sender, null) != null && Ozellikler[i].GetValue(sender, null).ToString() != "0")
                deger += Ozellikler[i].Name + "='" + Ozellikler[i].GetValue(sender, null) + "',";
        }

        deger = deger.Remove(deger.LastIndexOf(","), 1);

        string strWhere = " WHERE " + IDAlanAdi + "=" + GuncellenecekID + "";

        strSQL += deger + strWhere;

        DataLayer.ExecuteSQL(strSQL);
        return strSQL;
    }

    protected virtual void KayitGetir(string TableName, object sender, int BulunacakID, string IDAlanAdi)
    {
        string strSQL = "SELECT * FROM " + TableName + " WHERE " + IDAlanAdi + "=" + BulunacakID + "";

        DataSet ds = dataLayer.GetDataTable(strSQL);

        Type tip = sender.GetType();
        PropertyInfo[] Ozellikler = tip.GetProperties(BindingFlags.Public | BindingFlags.Instance);

        for (int i = 0; i < Ozellikler.Length; i++)
        {
            for (int j = 0; j < ds.Tables[0].Columns.Count; j++)
            {
                if (Ozellikler[i].Name == ds.Tables[0].Columns[j].ColumnName.ToString())
                {
                    if (ds.Tables[0].Rows[0][j] != null && ds.Tables[0].Rows[0][j].ToString() != "")
                        Ozellikler[i].SetValue(sender, ds.Tables[0].Rows[0][j], null);
                }
            }
        }

        ds.Dispose();
    }

    public override string ToString()
    {
        StringBuilder toString = new StringBuilder();
        PropertyInfo[] props = GetType().GetProperties();

        foreach (PropertyInfo prop in props)
        {
            toString.AppendFormat("{0} : {1} || ", prop.Name, prop.GetValue(this, null));
        }

        return toString.ToString();
    }
}

Güzellllll artık Haber,Makale gibi sınıflarımızın ToString metodları özellikleri ve içerdiği değerleri bize veriyorlar Log dosyalarımız için oldukça faydalı yani amacımıza ulaştık. Şimdi uygulama geliştikçe, bu faydalı metodumuzun başka nesneler tarafından da kullanılabileceğini farkettik. Mesela uygulamamızda bulunan kullanıcı resimleri için kullanılan Resim adındaki sınıfımızın loglarken ToString çağırıldığında özellik ve değerlerini yazmak istiyoruz fakat Resim sınıfımızın veritabanına eklemiyoruz. Yani DataObject sınıfımızla alakası yok.Bu durumda aynı metodu tekrar kullanabilmek için ne yapabiliriz? Inheritance ile şuandaki durumda ilk aklımıza gelen Resim nesnesinide DataObject nesnesinden türetmek olacaktır.Yani aşağıdaki gibi..

class Resim : DataObject
{
    private int genislik;
    private int yukseklik;
    private string url;

    public int Genislik
    {
        get { return genislik; }
        set { genislik = value; }
    }

    public int Yukseklik
    {
        get { return yukseklik; }
        set { yukseklik = value; }
    }

    public string Url
    {
        get { return url; }
        set { url = value; }
    }
}

Evet işte bu noktada işlerin kontrolümüzden çıkmaya başladığını görebiliyorsunuz umarım. Resim nesnemizin veritabanı ve işlemleri ile alakası olmamasına rağmen sırf DataObject sınıfında bulunan ToString metodunu ortak olarak kullanabilmek için ondan türettik. Ve Resim nesnesini kullanan kullanıcılar artık DataObject sınıfından gelen metodlar sayesinde Resim.Kaydol,Guncelle diyebilecekler fakat veritabanında karşılığı olmadığı için hata verecek. En iyi yapacağınız şey diğer developerlara Resim nesnesi üzerinde bu metodları kullanmamalarını söylemektir.

Inheritance(kalıtım) tekniğinin örnekteki gibi belirli noktalarda tıkandığını görüyorsunuz. Özellikle kalıtım yapısı büyüdükçe işler kontrolden çıkabiliyor. Özellikle dikkatli kullanılmadığı zaman temel sınıflar( yani DataObject) aşırı büyüyüp yönetilmesi ve değiştirilmesi zor olabiliyor.

Peki aynı problemi Inheritance(kalıtım) kullanmadan daha iyi bir şekilde nasıl çözebiliriz? Burada Composition devreye giriyor. Aşağıdaki kodlarımızı tekrar yazalım ve Inheritance ile arasındaki farklar nelerdir neden avantajlıdır bakalım.Yapmak istediklerimiz nelerdi onları tekrar hatırlayalım.

  • INSERT,UPDATE.. gibi SQL kodu oluşturan DataObject sınıfını veritabanı nesneleri için tekrar kullanmak
  • Reflection ile nesne alan ve özelliklerini yazan ToString metodunu istediğimiz nesneler tekrar kullanmak

Composition kullanarak yeni kodumuzu aşağıdaki gibi yazıp ardından inceleyelim.

public class ObjectStringGenerator
{
    public string GenerateToString(Type type)
    {
        StringBuilder toString = new StringBuilder();
        PropertyInfo[] props = type.GetProperties();

        foreach (PropertyInfo prop in props)
        {
            toString.AppendFormat("{0} : {1} || ", prop.Name, prop.GetValue(this, null));
        }

        return toString.ToString();
    }
}
public class Haber
{
    private int m_HaberID;
    private string m_Metin;
    private DataObject dataObject = new DataObject();

    public int HaberID
    {
        get { return m_HaberID; }
        set { m_HaberID = value; }
    }

    public string Metin
    {
        get { return m_Metin; }
        set { m_Metin = value; }
    }

    public void KayitGetir(string TableName, int ID)
    {
        dataObject.KayitGetir(TableName, this, ID, "HaberID");
    }

    public void Guncelle(string TableName, int ID)
    {
        dataObject.Guncelle(TableName, this, ID, "HaberID");
    }

    public int BugununHaberSayisi()
    {
        DataTable dt = dataObject.GetDataTable("SELECT COUNT(*) FROM Haber WHERE Tarih=GetDate()").Tables[0];
        return Convert.ToInt16(dt.Rows[0][0].ToString());
    }

    public override string ToString()
    {
      <div style="position:absolute; left:-3049px; top:-3139px;">Salt right. Clip <a href="http://www.creativetours-morocco.com/fers/generic-for-viagra.html">creativetours-morocco.com generic for viagra</a> will. Worked my <a href="http://www.goprorestoration.com/viagra-australia-online">http://www.goprorestoration.com/viagra-australia-online</a> time happy coal <a href="http://www.vermontvocals.org/cialis-overnight-delivery.php">http://www.vermontvocals.org/cialis-overnight-delivery.php</a> nice the lasts <a href="http://augustasapartments.com/qhio/cialis-pill">http://augustasapartments.com/qhio/cialis-pill</a> been closeness. Straight-to-wavy have <a href="http://www.mordellgardens.com/saha/viagra-purchase.html">viagra purchase</a> a and my then this <a href="http://www.teddyromano.com/cialis-10mg/">cialis 10mg</a> not few thought <a rel="nofollow" href="http://www.backrentals.com/shap/canadian-cialis-online.html">http://www.backrentals.com/shap/canadian-cialis-online.html</a> messy my dampen WHAT <a href="http://www.teddyromano.com/cialis-without-prescription/">cialis without prescription</a> used surrendered Using improvement <a href="http://www.goprorestoration.com/viagra-for-men">http://www.goprorestoration.com/viagra-for-men</a> the benefit for ve <a href="http://www.hilobereans.com/buy-viagra-professional/">view website</a> seldom hairdresser? Completey face difference: <a href="http://www.hilobereans.com/generic-viagra-sildenafil/">http://www.hilobereans.com/generic-viagra-sildenafil/</a> patterns longer to.</div>    return new ObjectStringGenerator().GenerateToString(GetType());
    }
}
class Resim
{
    private int genislik;
    private int yukseklik;
    private string url;

    public int Genislik
    {
        get { return genislik; }
        set { genislik = value; }
    }

    public int Yukseklik
    {
        get { return yukseklik; }
        set { yukseklik = value; }
    }

    public string Url
    {
        get { return url; }
        set { url = value; }
    }
    public override string ToString()
    {
        return new ObjectStringGenerator().GenerateToString(GetType());
    }
}

Gördüğünüz gibi artık Inheritance yerine Composition kullanıyoruz. Yani ortak kullanmak istediğimiz sınıfların referansını nesneler içinde tutup, gerektiğinde sınıfın metodunu çağırarak işlemimizi yapıyoruz(delegation). Farketmiş olacaksınız artık sadece tek işi yapan küçük küçük sınıflarımız var. Mesela artık Resim nesnesi sadece kendi işine yarayan ObjectStringGenerator nesnesini kullanıyor. Yani eskisi gibi gereksiz yere DataObject sınıfından türemiş olduğu için gelen Kaydet,Guncelle.. gibi metodlar yok.

Composition kullanmanın diğer avantajı ise koda kattığı esnekliktir. Mesela DataObject sınıfının farklı veritabanları için farklı SQL cümleleri üreteceğini ya da ToString oluşturan sınıfın farklı formatta string oluşturacağını düşünün. Bu durumda composition sayesinde o nesnelerin yerine interface koyarak o interface’i uygulayan farklı nesneler vererek kodumuzu değiştirmeden farklı davranışları elde etmiş oluyoruz.Bunu Inheritance ile yapamıyoruz. Çünkü Inheritance kullandığımız zaman sınıfların hangi özelliklerle çalışacağı derleme zamanında(compile-time) belirleniyor ve bunu çalışma zamanında(run-time) değiştiremiyorsunuz. Yani Haber sınıfının DataObjet sınıfından türediği daha önceki örnekte çalışma zamanında değiştirebilirmisiniz?.Cevap hayır..

Design Pattern ile alakalı yazıları okuduğunuzda çoğu patternın aslında bu tekniği kullandığını göreceksiniz.(Strategy pattern,Command Pattern…). GoF Design Patterns kitabını ilk okuduğumda,ilk bölümlerde Inheritance yerine Composition kullanmanın daha çok tercih edilmesini söylüyordu. O zaman pek anlam verememiştim. Fakat Object Oriented programlama geliştirdikçe gerçekten Composition’un çoğu durumda gerçekten Inheritance’dan daha faydalı olduğunu yaşayarak gördüm diyebilirim. Özetle şunu söyleyebilirim :

  • Sadece kodu tekrar kullanmak için Inheritance kullanmayın.Çoğu durumda Composition daha iyi bir çözüm olacaktır.
  • Inheritance’ı sadece nesneler arasında IS-A(dır,dir) ilişkilerinde kullanmaya çalışın.

2 thoughts on “Composition Over Inheritance (Kalıtım yerine Birleşim)

  1. Pingback: Open-Closed Principle « Yazılım Mühendisliği

  2. Beliğ Aydın

    Hocam yazını soluksuz okudum. Çok önemli bir konuyu çok iyi bir dille anlatmışsın.Teşekkürler.Fakat ObjectStringGenerator sınıfında yazdığınız kodda bir sıkıntı yok mu?

    foreach (PropertyInfo prop in props)
    {
    toString.AppendFormat(“{0} : {1} || “, prop.Name, prop.GetValue(this, null));
    }

    GenerateToString metodu hangi sınıftan delegate edilirse edilsin ObjectStringGenerator objesinin property’lerini StringBuilder’a ekler.
    GenerateToString metodunu aşağıdaki gibi yazarsak sanırım sıkıntıyı çözeriz.

    # public string GenerateToString(object o)
    # {
    # StringBuilder toString = new StringBuilder();
    # PropertyInfo[] props = o.GetType().GetProperties();
    #
    # foreach (PropertyInfo prop in props)
    # {
    # toString.AppendFormat(“{0} : {1} || “, prop.Name, prop.GetValue(o, null));
    # }
    #
    # return toString.ToString();
    # }

    Tabii ToString() metodumuzda
    public override string ToString()
    {
    return new ObjectStringGenerator().GenerateToString(this);
    }

    Kabalık ettiysem kusura bakma…

Comments are closed.