21 Ekim 2011 Cuma

C# Olay Tabanlı Programlama

Uygulamalarımızda gerçekleşen bazı olayların, başka bir olayı tetiklemesini isteyebiliriz. Örneğin veri tabanından bir kayıt silindiğinde, o kaydı başka bir tabloya kaydetmek gibi ihtiyaçlarımız doğabilir. SQL tarafında bu tarz durumlarda kullandığımız yapı trigger’lardır. C# tarafında da bu olayları gerçekleştirmek için olay tabanlı programlama yapabiliriz. Temel amaç; nesnenin, bir olay meydana geldiğinde, diğer nesnelere haber vermesini sağlamaktır. Bu işlemi delegeler yardımı ile yaparız.

Urun adında bir sınıfımız olsun. Herhangi bir Urun nesnesinin fiyatı değiştiğinde, durumdan haber almamızı sağlayan bir mekanizmayı tetikleyeceğimizi düşünelim. Aklımıza ilk olarak program içerisinde fiyata değer atandığı yerlerde uyarı koymak gelebilir. Ancak uygulama içerisinde 100 farklı bölgede fiyatın değiştiğini düşünürsek, her yerde aynı işlemi yapmak zorunda kalırız. Ayrıca, uyarı mesajında herhangi bir değişiklik yapılması gerektiğinde, 100 farklı yerde tek tek düzeltme yapmamız gerekecektir. Dolayısı ile bu tarz yöntemlerden kaçınmalıyız.

static void Main(string[] args)

        {

 

            Urun urn = new Urun(1, "Monitor", 200);

 

            urn.Fiyat = 250;

            Console.WriteLine("Ürünün fiyatı değişti...");

 

            //.................

 

            urn.Fiyat = 350;

            Console.WriteLine("Ürünün fiyatı değişti...");

 

            //.................

 

            urn.Fiyat = 450;

            Console.WriteLine("Ürünün fiyatı değişti...");

 

        }

Görüldüğü gibi, 3 farklı yerde aynı işlemi tekrarladık. Bu sadece sembolik bir örnekti. Büyük projelerde yazdığımız tekrarlı kodlar sıkça başımızı ağrıtacaktır.

Fiyatın değişmesi set bloğunda gerçekleşeceği için ekrana yazdırma işlemini burada gerçekleştirerek yukarıdakine göre daha iyi bir kodlama yapabiliriz.

public delegate void FiyatHandler();

 

    public class Urun

    {

        int _id;

        string _ad;

        double _fiyat;

 

        public int Id

        {

            get { return _id; }

            set { _id = value; }

        }

 

        public string Ad

        {

            get { return _ad; }

            set { _ad = value; }

        }

 

        public double Fiyat

        {

            get { return _fiyat; }

            set

            {

                _fiyat = value;

 

                Console.WriteLine("Ürünün fiyatı değişti...");

            }

        }

 

        public Urun(int id, string ad, double fiyat)

        {

            Id = id;

            Ad = ad;

            Fiyat = fiyat;

        }

    }

Burada da başka bir sıkıntı devreye girmektedir. Yukarıdaki tasarım ile Urun sınıfı Console uygulamasına bağımlı hale gelmiş oldu. WriteLine metodundan dolayı, Urun sınıfını Windows veya Web’de istediğimiz doğrultuda kullanamıyor olacağız.

İşte tam bu noktada olay tabanlı programlama imdadımızı yetişir. Delegeler yardımı ile kolaylıkla tetikleme işlemlerini yapabiliriz.

Olay tabanlı programlama için sırası ile yapılması gerekenler;

  1. Metodumuzun imzasına uygun bir delege tanımlamak.
  2. Tasarladığımız sınıfın içerisine, o delegenin tipinden private bir field tanımlamak.
  3. Tanımlanan private alanı dışarıya açmak. (Bir field’ı get ve set metotları ile dışarıya açmak gibi düşünebiliriz. Burada delegemize metot bağlayacağımızdan ötürü, ilgili delegeye metot ekleyen ve çıkaran iki adet public metot söz konusudur.)
  4. Olayın meydana geleceği yerde delegenin içerisindeki metodu tetiklemek.

Yukarıda bahsettiğimiz örnek için, fiyat değiştiğinde uyarı verecek olan metot parametre almasın ve geriye değer döndürmesin. Urun sınıfımızı aşağıdaki gibi tasarlayalım.

public delegate void FiyatHandler();

 

    public class Urun

    {

        int _id;

        string _ad;

        double _fiyat;

        FiyatHandler FiyatDegistigindeCalistirilmasiIstenenMetotlariTutanCanta;

 

        public int Id

        {

            get { return _id; }

            set { _id = value; }

        }

 

        public string Ad

        {

            get { return _ad; }

            set { _ad = value; }

        }

 

        public double Fiyat

        {

            get { return _fiyat; }

            set

            {

                _fiyat = value;

 

                if (FiyatDegistigindeCalistirilmasiIstenenMetotlariTutanCanta != null)

                {

                    FiyatDegistigindeCalistirilmasiIstenenMetotlariTutanCanta.Invoke();

                }

            }

        }

 

        public Urun(int id, string ad, double fiyat)

        {

            Id = id;

            Ad = ad;

            Fiyat = fiyat;

        }

 

        public void CantayaEkle(FiyatHandler metot)

        {

            FiyatDegistigindeCalistirilmasiIstenenMetotlariTutanCanta += metot;

        }

 

        public void CantadanCikar(FiyatHandler metot)

        {

            FiyatDegistigindeCalistirilmasiIstenenMetotlariTutanCanta -= metot;

        }

    }

 

Fiyatın değişmesi, set bloğu içerisinde gerçekleştiğinden ötürü metodu tetikleme işini burada yaptık. Şimdi de program tarafında bir Urun nesnesi örnekleyerek test edelim.

class Program

    {

        static void Main(string[] args)

        {

            Urun urn = new Urun(1, "Monitor", 200);

 

            urn.CantayaEkle(FiyatDegistigindeTetiklenecekMetot);

 

            urn.Fiyat = 250; //1. değişiklik

            urn.Fiyat = 350; //2. değişiklik

            urn.Fiyat = 450; //3. değişiklik

        }

 

        static void FiyatDegistigindeTetiklenecekMetot()

        {

            Console.WriteLine("Ürünün fiyatı değişti...");

        }

    }

 

Ekran çıktımız aşağıdaki gibi olacaktır.

image

Burada aklımıza bir soru takılabilir. Urun nesnesini örneklerken yapıcı metodu içerisine parametre verdiğimizde de Fiyat property’si içerisindeki set bloğu çalışır. Burada da “Fiyat değişti” ibaresini görmeyi bekleyebiliriz. Ancak yaptığımız if kontrolü ile, eğer çanta dolu ise metodun tetiklenmesini sağladık. Nesneyi örneklediğimiz anda metodu taşıyan çantanın içi boş olduğundan if bloğunun içerisine girilmeyecektir.

Bu örnekten de anladığımızı gibi, olay tabanlı programlamanın son derece basit şekilde uygulanabildiğini gördük. Bu işlemi bir delege yardımı ile set bloğunda yaparak bazı avantajlar elde etmemiz de mümkündür.

  1. Bu sınıfı referans ederek kullanan her yerde aynı iş yapılır. Dolayısıyla işi iptal etmek ya da değiştirmek gibi şeyler yapmamıza gerek kalmaz.
  2. Tetikleme işlemini windows, console, web vb uyglamasında kullanmak istediğimizde, uygun bir metot tanımlamak yeterli olacaktır.

Daha anlaşılır olması açısından benzer bir örnek ile devam edelim. Personel adında bir sınıfımız olsun. Herhangi bi personelin maaşı değiştiğinde uyarı mesajı yazalım. Ancak bu uyarı mesajı eski maaşı ve yeni maaşı da içersin.

    public delegate void PersonelHandler(double x, double y, Personel personel);

 

    public class Personel

    {

        int _id;

        string _ad;

        double _maas;

        PersonelHandler _metotTasiyanCanta;

        string _departman;

        DateTime _tarih;

 

        public Personel(int id, string ad, double maas, string departman, DateTime tarih)

        {

            ID = id;

            Ad = ad;

            Maas = maas;

            Departman = departman;

            Tarih = tarih;

        }

 

        public DateTime Tarih

        {

            get { return _tarih; }

            set { _tarih = value; }

        }

 

        public string Departman

        {

            get { return _departman; }

            set { _departman = value; }

        }

 

        public int ID

        {

            get { return _id; }

            set { _id = value; }

        }

        public string Ad

        {

            get { return _ad; }

            set { _ad = value; }

        }

        public double Maas

        {

            get { return _maas; }

            set

            {

                double eskiMaas = _maas; //değiştirilmeden önceki maaş bilgisini alıyoruz.

                _maas = value; //atanan yeni değer, yeni maaş bilgisidir.

 

                if (_metotTasiyanCanta != null)

                {

                    //this anahtar sözcüğü, Maas property'si hangi nesne örneği üzerinden çağırılıyorsa onu temsil etmektedir. Burada Maas'ı değişecek personeli parametre geçmiş olduk.

                    _metotTasiyanCanta.Invoke(eskiMaas, value, this);

                }

            }

        }

 

        public void CantayaEkle(PersonelHandler metot)

        {

            _metotTasiyanCanta += metot;

        }

 

        public void CantadanCikar(PersonelHandler metot)

        {

            _metotTasiyanCanta -= metot;

        }

    }

Program tarafında testimiz yapalım.

class Program

    {

        static void Main(string[] args)

        {

            Personel prs = new Personel(1, "Onur", 5000, "yazılım", DateTime.Now);

            prs.CantayaEkle(MaasDegisti);

            prs.Maas = 3000;

            prs.Maas = 7000;

        }

 

        static void MaasDegisti(double eskiMaas, double yeniMaas, Personel personel)

        {

            Console.WriteLine("'{0}' adlı personelin Maaşı değişti... Eski Maaş:{1} => Yeni Maaş:{2}", personel.Ad, eskiMaas, yeniMaas);

        }

    }

Ekran çıktımız aşağıdaki gibi olacaktır.

image

Tetikleme işlemleri yapabilmemiz için .Net’in bize sunduğu event yapısını kullanmak işimizi kolaylaştıracaktır. Ancak temel mantığı anlamak için bu tarz örneklerin incelenmesinde fayda var diye düşünüyorum. Event kullanımını şuradan inceleyebilirsiniz. Faydalı olması dileği ile…



2 yorum:

Ali Aydoğmuş dedi ki...

Oldukça yararlı bir yazı ,teşekkürler...

http://oktayaltan.blogspot.com/ dedi ki...

Emek harcayarak yazmışsın terine sağlık...

Yorum Gönder