Composition ile Aggregation Arasındaki Fark

Uygulama geliştirirken OOP prensiplerine yaklaştıkça nesneler arasındaki ilişkilerin önemi de gitgide artıyor.  Ben de tasarım desenlerine verdiğim kısa arada çok karıştırılan iki kavramdan bahsetmek istiyorum. Nesneler arasındaki iki ilişki türü : Aggregation, Association

Aggregation : Bu tür ilişki de nesnelerin yaşam döngüleri birbirlerinden ayrıdır. Bir nesne diğerinden bağımsız olarak da yaşamını sürdürebilir. Yani aralarında bir sahiplik ilişkisi (has-a ) vardır. Örneğin Dizüstü Bilgisayarınız ile onun çantası arasında böyle bir ilişki vardır. Çantayı ayrı olarak ya da laptop’u ayrı olarak düşünebiliriz. Yaşam döngüleri ortak değildir. UML diagramında gösterimi ve koda dökülmüş bir örneği aşağıdaki gibidir.

public class Canta
{

}
public class Laptop {
private Canta _canta;
public Laptop(Canta canta)
{
_canta = canta;
}
}

Composition : Bu tür ilişki de nesnelerin yaşam döngüleri birbirleriyle bağlantılıdır. Bir nesne diğerinden bağımsız olarak kullanılamaz. Aralarındaki ilişki parçası olma(is-a-part-of ) ilişkisidir. Az önceki örneğimizden gidersek dizüstü bilgisayarımız ile ekranı arasında bu tarz bir ilişki vardır. UML diagramında gösterimi ve koda dökülmüş bir örneği aşağıdaki gibidir.

public class Ekran
{

}
public class Laptop
{

Ekran _ekran = new Ekran();
}

Observer Design Pattern

Şu aralar daha önceden üzerinden geçtiğim tasarım desenleri ile uğraşıyorum tekrar. Ama bu sefer biraz daha pratik şekilde ilerlemeye çalışıyorum. İlerdikçe de burada hem bana ilerde hatırlamam için not olması hem de ihtiyacı olanların faydalanması için paylaşmaya çalışıyorum.

Son yazımda Strateji tasarım deseninden bahsetmiştim. Bu kez ise Observer tasarım deseninden bahsetmeye çalışacağım. Örneğin bir alışveriş sitesinin kampanyalarından haberdar olmak için maillist’ine kayıt olduğunuzu düşünün, daha sonra bir başkasının da aynı listeye abone olduğunu düşünelim. Bir kampanya olduğunda siteden abone olan herkese bu kampanya gönderilir.  Daha sonradan artık kampanya bilgilerini almak istemezseniz listeden çıkarsınız ve artık yeni kampanya bilgileri diğer abonelere gönderilir.

Observer tasarım deseni de uygulamalarımızdaki bu tarz işlemlerimizi yapmamızı kolaylaştıran bir desendir. Örneğin bir nesnenin durumu değiştiğinde uyarılacak nesneler listesine eklenmiş nesnelerimiz otomatik olarak uyarılır.

Bu desen ile ilgili örneğimi bir önceki strateji deseni örneğim üzerinden yapacağım. Böyle olması biraz daha karışık olmasına sebep oldu ama ezbere kaçmayı önlememi sağladı. Ayrıca iki deseni bir örnek de birleştirmiş oldum. Öncelikle eklememiz gereken yeni arayüz ve sınıfları vereceğim, daha sonra değişiklik yapmamız gereken kısımlardan bahsedeceğim.

İlk olarak biri yayıncı(subject) biri de dinleyici (subscriber,observer) olmak üzere iki interface tanımlayacağım. Daha sonra kullanacağım bütün dinleyici ve yayıncı sınıfları bu sınıflardan türeteceğim.

public interface IYayinci
{
void dinleyiciKaydet(IDinleyici dinleyici);
void dinleyiciCikar(IDinleyici dinleyici);
void dinleyicileriUyar();
}
public interface IDinleyici
{
void guncelle(ArrayList args);
}

Burada IYayinci arayüzümüzde üç adet metodumuz var bunlar uyarılacak nesneleri eklememizi, çıkarmamızı ve nesnenin durumu değiştiğinde listemizdeki nesnelerin uyarılmasını sağlıyor. IDinleyici arayüzündeki metodumuz ise durum değişikliği olduğunda yapılması gereken işlemleri içeren metoddur.

İkinci olarak yayıncı sınıfımızı oluşturuyoruz. Bu sınıfımızda arayüzden aldığı metodları implemente ediyoruz. Ayrıca ben bir adet renkDegisti metodu ekledim ki bu metodu da renk değişimi olduğunda kullanacağız.

public class RenkYayinci : IYayinci
{
ArrayList _dinleyiciler;
ArrayList _args;
public RenkYayinci()
{
_dinleyiciler = new ArrayList();
_args = new ArrayList();
}
public void dinleyiciKaydet(IDinleyici dinleyici)
{
_dinleyiciler.Add(dinleyici);
}
public void dinleyiciCikar(IDinleyici dinleyici)
{
int i = _dinleyiciler.IndexOf(dinleyici);
if (i >= 0)
{
_dinleyiciler.RemoveAt(i);
}
}
public void dinleyicileriUyar()
{
for (int i = 0; i < _dinleyiciler.Count; i++)
{
IDinleyici dinleyici = (IDinleyici)_dinleyiciler[i];
dinleyici.guncelle(_args);
}
}
public void renkDegisti(string renk)
{
_args.Clear();
_args.Add(renk);
dinleyicileriUyar();
}
}

Burada renkDegisti metoduna renk değişkenini almama rağmen bunu arraylist’e atma sebebim IDinleyici arayüzünün sadece renk için özelleşmesini engellemek ve daha sonra da kullanmak istememdir. Daha sonra dinleyicileriUyar() metodunu çağırıyoruz ve bütün ekli nesneleri haberdar ediyoruz.

Artık eski sınıflarımızda değişiklik yapmaya başlayabiliriz. Öncelikle Daire ve Kare Sınıflarımızı aşağıdaki şekle getiriyoruz.

public class Kare : Sekil, IDinleyici
{
private RenkYayinci _renkYayinci;
public Kare(Dictionary<string, int> degerler, RenkYayinci renkYayinci)
{
_cevre = new KareCevre();
_degerler = degerler;
this._renkYayinci = renkYayinci;
this._renkYayinci.dinleyiciKaydet(this);
}
public void guncelle(ArrayList args)
{
Console.WriteLine(“\nKare kenar renkler değişti ” + args[0].ToString());
}
}

 

public class Daire : Sekil, IDinleyici
{
private RenkYayinci _renkYayinci;
public Daire(Dictionary<string, int> degerler, RenkYayinci renkYayinci)
{
_cevre = new DaireCevre();
_degerler = degerler;
this._renkYayinci = renkYayinci;
this._renkYayinci.dinleyiciKaydet(this);
}
public void guncelle(ArrayList args)
{
Console.WriteLine(“\nDaire çember içi renkler değişti” + args[0].ToString());
}
}

Sınıf tanımlamalarımızda koyu ile işaretli yerleri değiştiriyoruz. Burada öncelikle constructor metodumuzda parametre olarak aldığımız yayıncıya nesnemizi ekliyoruz. Daha sonra dinleyici arayüzünden alınan guncelle metodunu implemente ediyoruz.

Artık desenimizi deneyebiliriz. Önceki yazımızda bulunan örnekteki main metodunu aşağıdaki şekilde değiştiriyoruz.

static void Main(string[] args)
{
Dictionary<string, int> dd = new Dictionary<string, int>();
dd.Add(“BirKenar”, 4);
RenkYayinci renkYayinci = new RenkYayinci();
Sekil sk = new Kare(dd, renkYayinci);
renkYayinci.renkDegisti(“mavi”);
Console.WriteLine(sk.cevreHesapla(dd).ToString());
Console.ReadLine();
}

Programımızı derleyip çalıştırdığımızda karşımızda

Kare kenar renkler değişti mavi

16

çıktısını görebiliriz.

Strategy Design Pattern

Strateji tasarım deseni geliştirdiğimiz uygulama içinde algoritmaları sınıflandırmamızı ve çalışma anında kullanacağımız algoritmayı seçmemizi sağlar. Bu algoritmalar kendi içinde kapsüllenerek (encapsulate) programın geri kalanından soyutlanır ve uygulamamızın loosely coupled bir yapıda olmasına yardım eder. Örneğin bir maliyet hesabında LIFO mu yoksa FIFO mu kullanacağımızı çalışma anında belirlemek istiyorsak strateji tasarım desenini kullanarak bunu nesne yönelimli programlama ilkeleri doğrultusunda yapabiliriz.

Algoritmaların seçim işlemini if else blogları ile yapabilirdik. Ancak küçük bir değişiklikte uygulamamızın içine müdahale etmek zorunda kalırdık. Ayrıca yeni algoritmalar ekledikçe bu if else bloglarımız gittikçe uzayacaktı. Ya da işlemi kalıtım yolu ile de gerçekleştirebilirdik. Bir metodu üst bir sınıfta tanımlayıp, alt sınıflarda bu metodu override edebilirdik. Ancak bu durumda da bir değişiklik durumunda bütün alt sınıflarda kodu tekrar düzenlememiz gerekecekti. Ya da eklediğimiz her yeni sınıf için yeniden metodu override etmemiz gerekecekti. Bu durumda kodların yeniden kullanılabilirliği konusunda başarısız olacaktık.

Bu deseni nasıl uygulayabileceğimizi bir örnekle daha iyi gösterebiliriz. Ben nesne yönelimli programlama ile ilgili örnekler verirken daha anlaşılır ve daha uygulanabilir olması nedeniyle mümkün olduğunca şekiller üzerinden gitmeyi tercih ediyorum. Yine vereceğim bu örnekte Strateji tasarım desenini kullanarak Şekil sınıfından türeyen daire ve kare sınıflarının çevrelerini hesaplayan basit bir uygulama yazacağım.

Öncelikle çevre hesaplama algoritmalarını bir araya toplamam gerekiyor. Kapsülleme işlemini gerçekleştirmek ve çevre hesaplama algoritmalarını belli bir formatta tutmak için öncelikle bir interface yazıyorum.

public interface ICevre
{
int hesapla(Dictionary<string, int> degerler);
}

Bundan sonra çevre hesaplama algoritmalarımı tutacak bütün sınıfları bu interface’den türeteceğim. Bu interface’imiz bir adet hesapla metodu içeriyor. Interface’imizden sonra ise çevre hesaplayacak sınıflarımızı oluşturacağız.

public class DaireCevre : ICevre
{
private int cevresi = 0;
public int hesapla(Dictionary<string, int> degerler)
{
cevresi = 2*degerler[“Pi”]*degerler[“r”];
return cevresi;
}
}

DaireCevre sınıfımız ICevre interface’inden aldığı hesapla metodunu implemente ediyor. Aynı şekilde KareCevre sınıfını da yazıyoruz. Bu sınıf da kendine özgü algoritmasıyla çevre hesaplamasını yapıyor.

public class KareCevre : ICevre
{
private int cevresi = 0;
public int hesapla(Dictionary<string, int> degerler)
{
cevresi = degerler[“BirKenar”]*4;
return cevresi;
}
}

Artık çevre ile ilgili hesaplamalarımızı yaptığımıza göre kullanacağımız sınıfları yazmaya geçebiliriz. İlk olarak şekillerimi türeteceğim bir Sekil sınıfı yazıyorum.

public class Sekil
{
public ICevre _cevre;
public Dictionary<string, int> _degerler;

public int cevreHesapla(Dictionary<string, int> degerler)
{
return _cevre.hesapla(degerler);
}
}

Bu sınıfımızda bütün algoritma sınıflarımızı kapsayabilmesi için bir adet ICevre interface’i nesnesi oluşturuyoruz. Böylece loosely coupled özelliğini de sağlamış oluyoruz. Yeni ekleyeceğimiz, çıkaracağımız ya da değiştireceğimiz algoritmalar uygulamanın geri kalanını etkilemiyor. Ayrıca parametreler için bir adet Dictionary nesnesi tanımladım. Burada gördüğümüz cevreHesapla metodu bizim dışarıdan erişeceğimiz ve hesaplama işlemlerini yaptıracağımız metod. Kendi içinde hangi şeklinde çevresi hesaplanacak ise ona ait metodu çağırıyor.

Artık kullanacağımız sınıfları yazabiliriz.

public class Kare : Sekil
{
public Kare(Dictionary<string, int> degerler)
{
_cevre = new KareCevre();
_degerler = degerler;
}
}

public class Daire : Sekil
{
public Daire(Dictionary<string, int> degerler)
{
_cevre = new DaireCevre();
_degerler = degerler;
}
}

Bu sınıflarımız da constructor metod çağrıldığında ICevre türünden olan nesnemize ilgili cevre sınıfı nesnesi(DaireCevre, KAreCevre) oluşturularak atanıyor. Böylece doğru hesapla fonksiyonunun çağrılması sağlanıyor.

Son olarak uygulamamızı deneyeceğimiz kısmı yazabiliriz.

static void Main(string[] args)
{
Dictionary<string,int> dd = new Dictionary<string,int>();
dd.Add(“BirKenar”,4);
Sekil sk = new Kare(dd);
Console.WriteLine(sk.cevreHesapla(dd).ToString());
Console.ReadLine();
}

Böyle bir denemede ekrana 16 yazacaktır.

String Birleştirme Ve Performans

Uygulamalırımızda en çok kullandığımız değişken tiplerinden bir tanesi de string’dir. Bir çok işlemimizi string’ler üzerinde yaparız. Ancak strinf değişkenleri kullanırken dikkat etmemiz gereken ve bize performans açısından büyük katkı sağlayacak bir kaç nokta vardır.

Hepimiz uygulamalarımızda

 string x = “”;
        for (int i=0; i <= 100000; i++)
        {
            x += i.toString();
        }

tarzında bir string birleştirme işlemi gerçekleştirmişizdir. Bu tarz bir string birleştirme işlemi küçük döngülerde bir problem oluşturmazken büyük döngülerde başımıza ciddi performans problemleri çıkarabilir. Bunun nedeni ise böyle bir birleştirme işleminde bütün atama işlemlerinde yeni bir nesne oluşturulmasıdır. Yani döngümüzün ilk tekrarında x değerini alır, yeni bir string değişken oluşturur. Eski değeri yeni değişkene atar ve sonrasında verdiğimiz string değeri buna ekler. Bir sonraki adımda yine yeni bir nesne oluşturulur, bu kez ikinci nesnedeki değer üçüncüye atılır ve adımlar bu şekilde devam eder. Yani yukarıdaki gibi bir döngü yazmış iseniz, sadece string birleştirme işlemi için 100000 ekstra nesne oluşturulur. Buna uygulamanıza esktra yük demektir.

Bu soruna çözüm ise StringBuilder. Yukarıdaki kodu aşağıdaki şekilde de yazabiliriz.

        StringBuilder builder = new StringBuilder();
        for (int i=0; i < 100000; i++)
        {
            builder.Append(i.toString());
        }
       string x = builder.ToString();

Kodumuzu bu hale getirdiğimizde bu kod parçası için yaklaşık 10 kat hız artışı sağlamaktadır.

Peki heryerde string oluşturma işlemlerimizi bu şekilde mi yapmalıyız. Tabii ki hayır. Örneğin

string tamad=ad+” “+soyad;

kod parçası yerine

 StringBuilder builder = new StringBuilder();
 builder.Append (ad);
 builder.Append (” “);
 builder.Append (soyad);
 string tamad= builder.ToString();

kod parçasını kullanmak bize bir performans artışı sağlamayacağı gibi okunurluğu da azaltacaktır.

DevExpress Kontrolleri SetEnabled Sorunu

DevExpress kontrollerini kullanırken eğer server tarafında yazdığınız kodda Enabled property’sini kullandıysanız. Örneğin

ASPxTextBox1.Enabled=true;

gibi, Client Side Eventler ‘de kontrolün enablad özelliğini değiştirmek istediğiniz de (SetEnabled fonksiyonu ile)  beklediğinizden farklı ve hatalı sonuçlarla karşılaşırsınız. Buna engel olmak için eğer bir kontrolün enabled özelliğini client side’da değiştireceksek ve server side’da da bu özelliğe müdahale etmemiz gerekiyorsa Enabled yerine ClientEnabled property’sini kullanmalıyız.

ASP.NET Control Bug

Son zamanlar da hep karşılaştığım sorunlardan bahsediyorum ama bu kez bahsedeceğim sorunun kaynağı ben değilim 🙂 Microsoft’ta kabul etmiş bug olduğunu. 

Asp.Net kontrolleri ile çalışırken, bir ASP.Net kontrolünden kendi kontrolümü türettim ve daha sonra sayfama eklemeye çalıştığımda

The type name “X” does not exist in the type “X”

tarzında bir hata alıyordum. Yanlış gözüken hiçbir şey yoktu ama hata veriyordu. Daha sonra – google sağolsun – aynı sorunla karşılaşan ve bunu bildiren birinin daha olduğunu öğrendim. Sorunun nedeni ise bir bug’mış. Namespace ile sınıf ismi aynı olduğu zaman veriyormuş bu hatayı, tam da benim yaptığım şey. Microsoft’tan gelen cevapta ise bunun bug olduğunu kabul etmişler ama ASP.NET 4’te bunu düzeltmeyeceklerini bildirmişler. Nedeni ise sorunun basit bir çözümü olmasıymış. Fazlasına burdan ulaşabilirsiniz.

EQATEC Profiler

          Bir programdan bahsetmek istiyorum bugün. Adı başlıktan da anlaşılacağı gibi EQATEC Profiler. Peki ne işe yarar bu program. Yazdığınız uygulamanın performansını gösterir diyebiliriz özetle. Daha doğrusu programınızda hangi nesneler kaç kez çağrılmış, bunlar ne kadar süre işlem gerçekleştirmiş, en fazla zaman harcanan kısım hangisi gibi bilgilere EQATEC Profiler sayesinde ulaşabiliyorsunuz. En güzel yanı ücretsiz olması, piyasada ücretli ya da ücretsiz daha iyi uygulamalar tabii ki vardır. Ama bu bana fazlasıyla yardımcı oldu.

        Peki ben nasıl tanıştım bu programla. Continue…

XPObject, XPBaseObject, XPCustomObject

        Bir keresinde bu üçü arasındaki fark hakkında bir soru gelmişti. Ben de cevabı başkalarına da faydası olabilir diye burada da yayınlamaya karar verdim.

Daha önce XPO‘dan bahsetmiştim.  XPO’da nesnelerimiz aracılığıyla verilerimizi veritabanına ekleyebilmek için nesnelerimizin kalıcı yani persistent olması gerekmektedir. Sınıflarımızı kalıcı yani persistent yapabilmek için XPObject, XPBaseObject, XPCustomObject’ten birinden türetmemiz gerekmektedir. Bunların türediği temelde bulunan yapı ise  XPBaseObject’tir.

XPObject,XPLiteObject,XPCustomObject XPBaseObject’ten türemektedir. Bunların arasında belirli özellik farkları var. Bu farktan bahsetmeden önce deferred deletion ve optimistic locking kavramlarını bilmeniz gerekmektedir. Optimistic locking birden fazla thread ile çalıştığınız durumlarda aynı veri üzerinde yapılan işlemlerde oluşabilecek problemleri engellemek için oluşturulmuş bir yapıdır. Deferred Deletion ise persistent bir nesneyi sildiğinizde xpo nun fiziksel olarak datayı hemen silmesini engeller ve silindi olarak işaretlenmesini sağlar. Deferred Deletion birden fazla nesnenin aynı nesneyi refere ettiği durumlarda veritabanı exceptionlarından korunmamızı sağlar. Bu dördünün bu özellikleri desteklemesi ile ilgili tabloda aşağıdaki gibidir.

Class Name            Deferred Deletion              Optimistic Locking
XPBaseObject                     –                                            +
XPLiteObject                      –                                             –
XPCustomObject               +                                            +
XPObject                            +                                            +

Statik Değişkenler ve Garbage Collection

Günümüzde uygulamalar büyüyüp, çalıstıkları cihazlar küçüldükçe bellek kullanımı daha da önemli hale geliyor. Ben de uygulamalarımızda bellek kullanımını azaltacak basit ve bilinen ama oldukça önemli bir kaç şeyden bahsetmek istiyorum. Bunlar statik değişkenler ve garbage collector.

Statik değişkenlerin normal değişkenlerden farkı statik değişkenlere ulaşmak için yeni bir nesne oluşturmamız gerekmez, sınıfımız üzerinden bu değişkenlere erişebiliriz. Ayrıca statik olmayan değişkenlerimiz her nesne ile ayrı olarak oluşturulur ve farklı degerlere sahip olur ama statik değişkenlerin tek bir değeri vardır. Örneğin uygulamamızda bir sınıftan sadece 3 tane nesne olusturulmasına izin vermemiz gerektiğini düşünelim. Eğer böyle bir durumda statik bir değisken kullanmasaydık, her nesne oluşturduğumuzda geçici nesneler oluşturup,, bu geçici değiskenlerin değerlerini artırıp bir yerde tutmamız gerekecekti. Bu da zaman ve bellek kaybı demek olurdu. Ancak statik değiskenler sayesinde tek bir değisken aracılığıyla kontrolümüzü sağlayabiliriz. Statik degişkenlerin mantığını anlamak için basit bir kod yazalım.
Öncelikle bir tane sınıf oluşturalım.
    
    class StatikDeneme
    {
        private int deger=0;
        public static int statikDeger=0;
 
        public int Deger
        {
            get
            {
                return deger; 
            }
            set
            {
                deger = value;
            }
        }

    }

Daha sonra formumuzun üzerine 2 tane buton sürükleyelim ve butonlaın altına sırayla şu kodları yazalım.

        private void button1_Click(object sender, EventArgs e)
        {
            StatikDeneme cs = new StatikDeneme();
            cs.Deger = 4;
            cs.Deger++;
            StatikDeneme.statikDeger++;
            MessageBox.Show(cs.Deger.ToString() + ”  ” + Class1.statikDeger.ToString());
        }

        private void button2_Click(object sender, EventArgs e)
        {
            StatikDeneme cs = new StatikDeneme();
            cs.Deger = 4;
            cs.Deger++;
            StatikDeneme.statikDeger++;
            MessageBox.Show(cs.Deger.ToString() + ”  ” + Class1.statikDeger.ToString());
        }

Gördügümüz gibi iki butonumuzun altındaki kod da aynı. Simdi uygulamamızı çalıştırıp rasgele butonlara basalım. Butonlara her basışımızda ilk (statik olmayan) değer değismezken, ikinci (statik olan) değerin arttığını görebiliriz. Bu şekilde statik bir değişkenle kolayca SingleTon desenini uygulayabiliriz.

Garbage Collector’lara gelince. GC’ler uygulama sırasında oluşturulmuş ancak kullanılmayan değiskenlerin yok edilmesini sağlar. Böylece nesneler hafızada gereksiz yer kaplamamış olur. GC’lar C# ta otomatik olarak çağrılır. Geliştirici bu zamanı bilemez ancak System.GC.Collect() metodu ile istedigi zaman bu işlemi gerçekleştirebilir. Yıkıcı metodlar (Destructor) nesnelerin yok edilmesinden hemen önce çağrılır. C++’da nesneler yok edilirken, bellekte oluşturulan dinamik alanlar iade edilmez. Bu nedenle geliştiricinin yıkıcı metod içinde bu alanlarıiade etmesi gerekir. C#’ta ise bu bellek alanlarının iadesi otomatik olarak yapılır. Bu nedenle yıkıcı metodlar genellikle statik veya global degiskenlerin değerlerini değiştirme amacı ile kullanılır. Örneğin uygulamamızda sadece 5 veritabanı bağlantısına izin vereceğimizi düşünelim. Bu durumda statik değişkenimizi yapıcı metodlarda bir artırırız ve 5’e ulaşıp ulaşmadığını kontrol ederek bağlantı nesnesinin oluşturulmasına izin veririz. Bağlantı nesnesinin yıkıcı metodunda ise statik değişsken değerimizi bir azaltırız. Böylece bir bağlantı nesnesi ne ihtiyaç kalmadığı zaman nesne yok edilirken, biz de bir bağlantı nesnesi hakkı daha kazanmış oluruz.