Category Archives: Architecture

Affet bizi SOA seni yanlış anladık

Geçenlerde danışman olarak bulunduğum bir firmada kısa bir Desing Patterns,Principles sunumu yapıyordum. Sunumun asıl sebebi geliştirilen büyük çaplı Java projesinde sınıflarında ortalama  20.000, 30.000 satırlardan oluşan gerçekten büyük sınıflar olmasıydı.Ve sınıflar neredeyse sadece statik metodlardan oluşuyordu. Şuana kadar çalıştığım projelerde en fazla 5000’i görmüştüm orda 30.000 satırı görünce artık 5000,10.000 satır kod içeren sınıflara küçük sınıf gözüyle bakmaya başladım diyebilirim :)

Hal böyle olunca bu konuda firmaya bu sayının çok büyük bir rakam olduğunu başlarının ağrımaması için düşürülmesinin iyi olacağını belirttik. Onlarda bizden neler yapılabilir bunun hakkında bir sunum yapmamızı istediler. Bende büyük sınıfların düşmanı olan Design Patterns, S.O.L.I.D Principles konularını uygulamalı olarak anlatan bir sunum yaptım.Sunumdan sonra birkaç ilginç diyalog yaşadım o yüzden sizlerlede paylaşmak istedim.

Yazılımcı arkadaşlardan birkaç tanesi projelerinin Service Oriented olduğunu bu yüzden benim anlattığım bu tarz problemleri önleyecek Object Oriented desing ve prensiplerin projelerinde uygulanamayacağını belirttiler. Biraz şaşırdım diyebilirim aslında. Çünkü SOA ve OOP hakkında büyük bir yanlış anlaşılma olmuş.

SOA yani Service Oriented Architecture bir uygulama mimarisidir.SOA uygulamayı birbirinden bağımsız, belirli fonksiyonlara bölünmüş, tekrar kullanılabilir olacak şekilde servisler halinde tasarlamaktır. SOA size uygulamanızı şu teknolojilerle,şu şekilde yapacağınızı söylemez altında yatan teknolojiyi istediğiniz gibi seçebilirsiniz. İster RMI, ister Web Servisleri,ister WCF kullanarak geliştirin bu size kalmış.

Object Oriented uygulama geliştirirken kullandığını bir teknik fakat SOA üst seviye bir uygulama mimarisi. Service Oriented bir uygulama geliştirirkende ister Functional Programming kullanın, isterseniz Object Oriented Programming kullanın servislerin ne ile geliştirildiğinin hiç bir önemi yok.Yani herhangi bir programlama teknolojisi gibi Object Oriented teknikleri SOA uygulama geliştirirken rahatlıkla kullanabiliriz. Bu yüzden Object Oriented Programming/Design ile SOA’yı karşılaştırmak elma ile armudu karşılaştırmak gibi birşey olsa gerek. Bu yüzden üzülerek SOA’dan bizi affetmesini istiyorum ve birdaha elma ile armudu karıştırmayacağımıza söz veriyorum :)

Model View Presenter (MVP) Pattern

Yine mimari olarak oldukça faydalalı olan tasarım kalıplarından birini örnekle incelemeye devam edeceğiz. Bu yazıda Model View Controller (MVC) Pattern’ın bir çeşidi olan Model View Presenter (MVP) Pattern nedir ne değildir bakıp örneklerle inceleyeceğiz.

Örnekleri buradan indirebilirsiniz

Model View Controller,Model View Presenter,Presentation Model… gibi GUI ile alakalı tasarım kalıplarının temel amacı kullanıcı arayüzü ile (UI) iş mantığının birbirinden ayrılmasını sağlamaktır.Yani yine sihirli kelimeyi tekrar edeceğim asıl amaç Separation of concerns . Herbiri bunu farklı şekilde yapar fakat felsefe temelde aynıdır. Şimdi her zamanki gibi kötü örnekle anlatmaya başlayalım. UI ile iş mantığının ayrılmamasının ne gibi problemi var? Ne güzel butonlara basıp altlarına bütün iş mantığı kodumuzu yazıyoruz değil mi? Bakalım problemler nelermiş…

Bunun için daha önceden DAO Pattern başlığı altında yazdığım yazıdaki örneği kullanacağım.İçeride kullanılan DAO sınıfları nedir, ne işe yarar bilmiyorsanız eski makaleyi okumanızda fayda var.  Burada küçük bir adres defteri uygulaması yapmıştık. Adres defterimizde kişi ekleme,silme,güncelleme,listeleme işlemleri yapıyorduk. Bu örnek için küçük programımızı biraz daha büyütelim ve daha önceden eksik olan birkaç özellik ekleyelim. Daha önceden eksik olan özelliklerden biri kişi eklerken herhangi bir validation kontrolü yapmamasıydı. Mesela adı, soyadı gibi alanlar boş girilebiliyordu. Ayrıca aynı ad ve soyada ait olan kişiler tekrar girilebiliyordu.Bu yüzden programa aşağıdaki gibi özellikler ekleyelim.

  • Ad,Soyad,Adres,Telefon gibi alanlar boş girildiğinde kayıt yapmayıp uyarı versin.
  • Aynı ad ve soyada sahip başka biri varsa kayıt yapmayıp uyarı versin.

Programımıza bu özellikleri ekleyip kodumuzu aşağıdaki gibi yazalım.

public partial class AdresListesi : Form
    {
        private readonly IKisiDAO kisiDao ;
        public AdresListesi(IKisiDAO dao)
        {
            kisiDao = dao;
            InitializeComponent();
        }

        private void btnKaydet_Click(object sender, EventArgs e)
        {
            if(txtAd.Text==string.Empty || txtSoyad.Text==string.Empty 
                || txtAdres.Text==string.Empty || txtTelefon.Text==string.Empty)
            {
                lblUyariMesaji.Visible = true;
                lblUyariMesaji.Text = "Bütün alanlar girilmeden kayıt edilemez!";
                return;
            }

            Kisi varOlanKisi = kisiDao.GetByName(txtAd.Text, txtSoyad.Text);
            if(varOlanKisi!=null)
            {
                lblUyariMesaji.Visible = true;
                lblUyariMesaji.Text = "Bu kişi listenizde mevcut tekrar kayıt edilemez!";
                return;
            }

            Kisi kisi = new Kisi(txtAd.Text, txtSoyad.Text, txtAdres.Text, txtTelefon.Text);
            kisiDao.Insert(kisi);
            LoadData();
        }

        private void LoadData()
        {
            dgKisiListesi.DataSource = kisiDao.GetAll();
        }

        private void AdresListesi_Load(object sender, EventArgs e)
        {
            LoadData();
        }

        private void Goster(Kisi kisi)
        {
            lblUyariMesaji.Visible = false;
            txtAd.Text = kisi.Ad;
            txtSoyad.Text = kisi.Soyad;
            txtAdres.Text = kisi.Adres;
            txtTelefon.Text = kisi.Telefon;
        }

        private void dgKisiListesi_Click(object sender, EventArgs e)
        {
            if (SeciliKisiID != 0)
            {
                Kisi kisi = kisiDao.GetByID(SeciliKisiID);
                Goster(kisi);
            }
        }

        private int SeciliKisiID
        {
            get
            {
                if (dgKisiListesi.CurrentRow != null)
                    return Convert.ToInt16(dgKisiListesi.CurrentRow.Cells["KisiID"].Value);
                return 0;
            }
        }

        private void btnGuncelle_Click(object sender, EventArgs e)
        {
            Kisi kisi = new Kisi(SeciliKisiID, txtAd.Text, txtSoyad.Text, txtAdres.Text, txtTelefon.Text);
            kisiDao.Update(kisi);
            LoadData();
        }

        private void btnSil_Click(object sender, EventArgs e)
        {
            kisiDao.Delete(SeciliKisiID);
            LoadData();
        }
    }

Çok basit bir uygulama olsa da özellikle yukarıda gördüğünüz kaydet butonunun(btnKaydet_Click) altına yazılan koda bakmanızı istiyorum.Gördüğünüz gibi iş mantığı(Business Logic) burada direk olarak butonun altında kodladık.Hem UI ile alakalı kodlar hemde iş mantığı ile alakalı kodlar bir arada.UI ile Business Logic arasında kesin bir ayrım yok iç içe geçmiş.Bu şekilde çok basit uygulamalar dışında  geliştirdiğiniz yazılımın yönetilmesi,bakımı gerçekten çok zor.Birde yukarıdaki yapının şekline bakalım

Buna benzer daha önceden çalıştığım bir projede Java Swing ile geliştirilmiş bir ekran(JFrame,.NET karşılığı Form diyebiliriz) yaklaşık olarak 3000 satırdı.Kodu değiştirmek,hata bulmak samanlıkta iğne aramaktan farksız değildi açıkçası.MVP,MVC uyguladıktan sonra yani UI ile iş mantığı ayrıldığında Proje sonlarına doğru aynı form yaklaşık olarak 200 satıra düşmüştü ve değiştirilmesi,yeniden kullanılması oldukça kolaylaştırılmıştı.

Ayrıca bu şekilde iş mantığı UI altına gömüldüğünde aynı iş mantığını başka yerlerde kullanmak çok zor.Bu iş mantığınızı başka biryere taşımak istediğinizde yapabileceğiniz tek ve en kötü şey olan copy-paste yapmak olacaktır. Tabi burada da UI ile iç içe olduğu için muhtemelen çalışmayacaktır. Yani aynı mantığı gereken başka yerlerde tekrar tekrar yazmanız gerekecek. Tabi sık sık değişin kullanıcı arayüzüne daha değinmedim.Kullanıcı arayüzünde Winforms da bulunan butonu değilde kendi yazdığınız değişik efektlere sahip butonunuzu kullanmak istiyorsunuz ne yapmanız lazım. Eski btnKaydet_Click altındaki kodları kopyala,yeni butonu koy, kopyaladığın kodları yeni butonun altına tekrar yaz…. gördüğünüz gibi birsürü problem var.

Şimdi bu tarz problemlerden kurtulmak için kullanılan design pattern’lardan biri olan Model View Presenter’ı kullanalım bakalım neler değişecek. Öncelikle kodu yazalım ardından detaylı şekilde inceleriz.

public partial class AdresListesi : Form,IViewAdresListesi
    {
        private AdresListesiPresenter presenter ;

        public AdresListesi()
        {
            InitializeComponent();
        }

        private void btnKaydet_Click(object sender, EventArgs e)
        {
            presenter.Save();
        }

        private void AdresListesi_Load(object sender, EventArgs e)
        {
            presenter.Init();
        }

        private void dgKisiListesi_Click(object sender, EventArgs e)
        {
            lblUyariMesaji.Visible = false;
            presenter.Select();
        }

        public string Ad
        {
            get { return txtAd.Text; }
            set { txtAd.Text = value; }
        }

        public string Soyad
        {
            get { return txtSoyad.Text; }
            set { txtSoyad.Text = value; }
        }

        public string Telefon
        {
            get { return txtTelefon.Text; }
            set { txtTelefon.Text = value; }
        }

        public string Adres
        {
            get { return txtAdres.Text; }
            set { txtAdres.Text = value; }
        }

        public string ErrorMessage
        {
            set
            {
                lblUyariMesaji.Visible = true;
                lblUyariMesaji.Text = value;
            }
        }

        public AdresListesiPresenter Presenter
        {
            set { presenter = value; }
        }

        public void Show(IList<k  ISI> kisiler)
        {
            dgKisiListesi.DataSource = kisiler;
        }

        public int SeciliKisiID
        {
            get
            {
                if (dgKisiListesi.CurrentRow != null)  return Convert.ToInt16(dgKisiListesi.CurrentRow.Cells["KisiID"].Value);
                return 0;
            }
        }

        private void btnGuncelle_Click(object sender, EventArgs e)
        {
            presenter.Update();
        }

        private void btnSil_Click(object sender, EventArgs e)
        {
            presenter.Delete();
        }
    }
public interface IViewAdresListesi
    {
        string Ad { get; set; }
        string Soyad { get; set; }
        string Telefon { get; set; }
        string Adres { get; set; }
        string ErrorMessage {set; }
        int SeciliKisiID { get; }
        AdresListesiPresenter Presenter { set; }
        void Show(IList<k  ISI> kisiler);
    }
public class AdresListesiPresenter
    {
        private readonly IViewAdresListesi view;
        private readonly IKisiDAO kisiDAO;

        public AdresListesiPresenter(IViewAdresListesi view, IKisiDAO kisiDAO)
        {
            this.view = view;
            this.kisiDAO = kisiDAO;
<div style="display: none"><a href='http://expositoryessaywriting.net/'>essays writing service</a></div>            view.Presenter = this;
        }

        public void Init()
        {
            UpdateView();
        }

        public void Save()
        {
            if(BosAlanVarmi())
            {
                view.ErrorMessage = "Bütün alanlar girilmeden kayıt edilemez!";
                return;
            }

            Kisi varOlanKisi = kisiDAO.GetByName(view.Ad,view.Soyad);
            if (varOlanKisi != null)
            {
                view.ErrorMessage = "Bu kişi listenizde mevcut tekrar kayıt edilemez!";
                return;
            }

            Kisi kisi = new Kisi(view.Ad, view.Soyad, view.Adres, view.Telefon);
            kisiDAO.Insert(kisi);
            UpdateView();
        }

        private bool BosAlanVarmi()
        {
            return view.Ad==string.Empty || view.Soyad==string.Empty 
                || view.Adres==string.Empty || view.Telefon==string.Empty;
        }

        public void Delete()
        {
            kisiDAO.Delete(view.SeciliKisiID);
            UpdateView();
        }

        public void Update()
        {
            Kisi kisi = new Kisi(view.SeciliKisiID, view.Ad, view.Soyad, view.Adres, view.Telefon);
            kisiDAO.Update(kisi);
            UpdateView();
        }

        public void Select()
        {
            if (view.SeciliKisiID == 0) return;
            Kisi kisi = kisiDAO.GetByID(view.SeciliKisiID);
            view.Ad=kisi.Ad;
            view.Soyad=kisi.Soyad;
            view.Adres=kisi.Adres;
            view.Telefon = kisi.Telefon;
        }

        private void UpdateView()
        {
            IList<k  ISI> kisiler = kisiDAO.GetAll();
            view.Show(kisiler);
        }
    }

ekranWin

Şimdi yukarıdaki koda bakacak olursanız View yani AdresListesi Windows Forms sınıfının oldukça basitleştiğini göreceksiniz. Aslında sadece AdresListesiPresenter sınıfının ilgili metodlarını çağırıyor ve kendi alanlarını get,set ediyor diyebiliriz. Başka ne DAO ne de Business Logic ile alakalı hiçbirşey bilmiyor.İş mantığı ile alakalı kodlar Presenter sınıfına taşındığı için View ile alakalı olmayan bütün kodlardan kurtulmuş olduk. Yani herkes kendi sorumluluğunu yerine getiriyor. AdresListesiPresenter sınıfına bakacak olursanız View ile Model yani iş mantığı sınıflarımızın koordinasyonunu kontrol ediyor. Sınıf diyagramına bakalım.

AdresListesiMVP

Şimdi bunu nasıl yapıyoruz ondan bahsedelim. Öncelikle AdresListesiPresenter Presenter altında tamamen ayrı bir modülde bulunuyor ve UI teknolojilerinden tamamen bağımsız. İçerisinde sadece ihtiyaç duyduğu IViewAdresListesi interface sınıfı bulunuyor.Bu interface üzerinde Presenter sınıfının çalışması için gerekli özellikler ve metodlar bulunuyor. Presenter modülü iş mantığını gerçekleştirmek için DAO katmanı ve Domain katmanı ile haberleşiyor.Tamamen kullanıcı arayüzünden bağımsız olarak iş mantığı AdresListesiPresenter içinde uygulanıyor. Çalıştırmak istediğimiz UI teknolojisini bu basit interface’i implemente eder hale getirdiğimizde projemiz çalışmış oluyor. Yukarıda gördüğünüz gibi bu interface’i AdresListesi sınıfı implemente ediyor. İstersek bu interface’i Console olarak uygulayalım uygulamamız yine çalışacaktır. Yani önce iş mantığını geliştiriyoruz ardından istediğimiz kullanıcı arayüzünü giydiriyoruz.MVP’nin genel yapısını aşağıdaki gibi ifade edebiliriz.

MVC_Mimari

Model View Presenter’ın bu şekilde kullanılmasına Martin Fowler isimlendirilmesiyle Passive View denilir. Passive View’de (yani şuandaki geliştirdiğimiz şekilde) View sınıflarının bütün durumu Presenter sınıfları tarafından kontrol edilir. Bu yapıda view olabildiğince sadedir. Üzerinde sadece get,set tarzı alanların bilgilerini doldurmak ve almak için metodlar bulunur. Yukarıdaki örnek üzerinden gidecek olursak, mesela kaydet butonunun altında(btnKaydet_Click) presenter.Save() metodu çağırılıyor. Ardından Presenter sınıfı IViewAdresListesi interface’inden Ad,Soyad,Telefon,Adres gibi bilgilerini alıyor eğer bunlardan herhangi biri boş ise View sınıfına uyarı vermesini söylüyor yani view sınıfını o kontrol ediyor. Ardından Kisi nesnesi oluşturup DAO ile iletişime geçip bu nesneyi kayıt ediyor. Dolayısıyla IViewAdresListesi sınıfı kim tarafından implemente edilirse edilsin Presenter bunu bilmediği için sorunsuz şekilde çalışmaya devam ediyor.Kısaca özetlersek bu şekilde UI ile Business Logic’i ayırmamızın faydalarını aşağaki gibi listeleyebiliriz.

  • Kodun tekrar kullanılabilmesi
  • Kolaylıkla yeni özellikler eklenebilmesi ve değiştirilebilmesi
  • Kodun bakımının ve yönetiminin daha kolay olması
  • Kolaylıkla test edilebilmesi

Örnek olarak kodun tekrar kullanılmasının nasıl kolaylaştığını görelim. Geliştirdiğimiz uygulamaya ASP.NET Webforms’un ne kadar kolay eklenebildiğini görelim. Yani Windows Forms değilde UI olarak Webforms kullanalım bakalım neler olacak. Bu projeyi ASP.NET e geçirmem için sadece ASP.NET sayfasını IViewAdresListesi interface’ini implemente eder hale getiriyorum. Kodları aşağıya yazıyorum.

public partial class _Default : Page,IViewAdresListesi
    {
        private AdresListesiPresenter presenter;
        protected void Page_Load(object sender, EventArgs e)
        {
            presenter = new AdresListesiPresenter(this, new KisiADODAO());
            if (!IsPostBack)
            {
                presenter.Init();
            }
        }

        public string Ad
        {
            get { return txtAd.Text; }
            set { txtAd.Text = value; }
        }

        public string Soyad
        {
            get { return txtSoyad.Text; }
            set { txtSoyad.Text = value; }
        }

        public string Telefon
        {
            get { return txtTelefon.Text; }
            set { txtTelefon.Text = value; }
        }

        public string Adres
        {
            get { return txtAdres.Text; }
            set { txtAdres.Text = value; }
        }

        public string ErrorMessage
        {
            set
            {
                lblUyariMesaji.Visible = true;
                lblUyariMesaji.Text = value;
            }
        }

        public int SeciliKisiID
        {
            get { return (int)ViewState["KisiID"]; }
        }

        public AdresListesiPresenter Presenter
        {
            set { presenter = value; }
        }

        public void Show(IList<k  ISI> kisiler)
        {
            gvKisiler.DataSource = kisiler;
            gvKisiler.DataBind(); ;
        }

        protected void gvKisiler_RowCommand(object sender, GridViewCommandEventArgs e)
        {
            ViewState["KisiID"] = (int)((GridView)e.CommandSource).DataKeys[Convert.ToInt32(e.CommandArgument)]["KisiID"];
            if (e.CommandName == "Select")
            {
                presenter.Select();
            }
        }

        protected void btnKaydet_Click(object sender, EventArgs e)
        {
            presenter.Save();
        }

        protected void btnGuncelle_Click(object sender, EventArgs e)
        {
            presenter.Update();
        }

        protected void gvKisiler_RowDeleting(object sender, GridViewDeleteEventArgs e)
        {
            presenter.Delete();
        }
    }

ekranWeb

Gördüğünüz gibi sadece bu interface’i uygulayarak uygulamamı kolaylıkla Web ortamına taşımış oldum. ASP.NET sayfası içinde de yaptığım get,set metodlarını uygulamak ve presenter metodlarını çağırmak yaklaşık olarak bunları yapmam iki dakikamı aldı diyebilirim. ASP.NET uyguladıktan sonra UML diyagramına bakalım.

AdresListesiMVP_Web

Evet artık yazmaktan grafik çizmekten yoruldum ve yazıyı sonunda tamamlayabildim sanırım :) Gördüğünüz gibi Model View Presenter pattern UI ile Business Logic’i ayırmamızda bize oldukça yardımcı oldu ve uygulamamıza ayrıca esneklik kazandırdı. Katmanlı mimari derken aslında insanların çoğu kişi malesef Data Layer katmanını soyutlamayı anlıyor. Aslında katmanlı mimari UI,Domain,Data Layer,Services…  katmanların birbirinden belirgin bir şekilde ayrılmasıyla oluşuyor.Bu bakımdan MVP bize oldukça fayda sağlıyor. Ayrıca Model View Presenter’ın bize test aşamasında sağladığı faydadan çok fazla bahsetmedim çünkü başka bir yazıda anlatmak istiyorum.Sağlıcakla kalın….

Data Access Object Pattern (DAO)

Bu yazıda biraz daha yüksek seviyeli tasarım kalıplarından olan Data Access Object Pattern kısa adıyla DAO pattern”ı inceleyeceğiz. Aslında DAO Pattern klasik anlamıyla daha önce bahsettiğimiz Strategy Pattern “ın örneğidir. Fakat uygulama alanı biraz daha veri katmanı ile özelleşmiştir.Makale için DAO Pattern kullanmadan ve kullanarak geliştirdiğim örnek projeleri yazının en sonunda bulabilirsiniz.

Örneklerde kodu fazla uzatmamak için parametrik SQL yapısı kullanmadım.Siz siz oldun gerçek projelerde örnekte yaptığım gibi SQL”i direk olarak kullanmayın. Ayrıca yazısı yine fazla uzatmamak için Dao sınıflarının direk formların altından kullandım.Daha iyi bir mimari açısından Dao sınıflarının kullanıcı arayüzünden(UI) kullanılması iyi bir pratik değil.Bununla alakalı bu yazıdan sonra Model View Presenter yazıma bakabilirsiniz.

DAO pattern”ın amacı sistemimizde kullandığımız nesnelerin çeşitli veri katmanlarına erişimini sistemden soyutlamaktır. Örnek olarak klasik veritabanı kullanan çoğu uygulamada nesneleri veritabanından okuma,silme,güncelleme,ekleme(CRUD) işlemleri bulunur.Tabi artık uzay çağında iş mantığınızın DataSet,RecordSet,DataTable.. gibi yapılar üzerine kurulu olmadığını varsayıyorum :) İş mantığının nesneler üzerine kurulu bir yapıda olduğunuzu varsayıp devam ediyorum.

Nesneleri kullanan uygulamalarda bu nesneleri veri kaynağına(XML,İlişkisel Veritabanı,Nesne Veritabanı…) girmek,silmek,güncellemek isteyeceksiniz. Yukarıda parantez içini okuduğunuzda bile zaten veri saklama teknolojilerinin ne kadar çeşitli olduğunu görüyorsunuz.Birde bunun üstüde değişen veri erişim teknolojileri gelince günümüz iş uygulamalarının en çok değişen kısımlarının veri erişim katmanları oluyor. Tabi bu veri erişim kodunu uygulamanızdan iyi soyutlamayınca yeni bir veri erişim teknolojisine geçmek çok zahmetli oluyor.Ayrıca iyi soyutlanmamış bir katman birçok diğer konuda(kodun yönetimi,testi…) başınıza birsürü dert açabiliyor.

Örnek olarak Java ve .NET de değişen veri teknolojilerine bakalım.

Java Veri Erişim Teknolojileri: JDBC,JDO,Hibernate,iBatis….
.NET Veri Erişim Teknolojileri: ADO.NET,Entity Framework,Enterprise Library,NHibernate,iBatis…

Gördüğünüz gibi bu teknolojiler sürekli değişiyor o yüzden uygulamamızın değişen veri erişim tekniklerinden etkilenmemesini istiyoruz işte bu noktada nesnelerimizin veri erişim katmanındaki CRUD(Create,Read,Update,Delete) ve diğer.. işlemlerini soyutlamak için DAO Pattern kullanıyoruz.Şimdi DAO Pattern”ı anlatmaya başlamadan aklınıza şöyle bir soru gelmiş olabilir. “Benim veri erişim teknolojim süper asla değiştirmeye ihtiyaç duymam. Neden DAO Pattern kullanmama gerek olsun ki…” (ne kadar ısrar etsenizde yazılımda değişmeyen tek şey değişimdir :) ). Aslında bence DAO tasarım kalıbının en büyük avantajı teknolojiniz değişmeyecek olsada veri erişim kodlarının uygulamadan soyutlanması,kodun yönetimini,esnekliğini oldukça arttırmaktadır.Yani kısaca Single Responsibility Principle ve Seperation Of Concerns temeline uymasını sağlıyor. Butonların altına SQL kodu yazdığım günler gözümün önünden film şeridi gibi geçti de:). Lafı fazla uzattım biliyorum hemen uygulamaya geçelim.Yine süper bir senaryo uyduralım.

Senaryo

Kullanıcının ekrandan ad,soyad,telefon,adres bilgilerini girerek kişileri silme,güncelleme,listeleme,ekleme yaptığı küçük bir adres defteri yapıyoruz.

Öncelikle ben hiç pattern mattern dinlemem gerilla tarzı kodlarım. Dalarım kodun içerisine basarım buton_click olayının altına yazarım modunda kodlamaya başlayalım.Yani DAO Pattern kullanmadan ya da veri katmanına erişimi soyutlamadan kodumuzu yazalım.Bu arada kodu yazarken fazla uzamaması için hata koşullarına,istisna durumlarına dikkat etmedim.Projeleri SQLite veritabanı kullanarak geliştirdim,Yönetim için SQLite2008 Pro Enterprise Manager kullandım sizde kullanabilirsiniz.Örnek amacıyla geliştirilmiş bir kod olduğu için gerçek bir projede dikkat edilmesi gereken birçok diğer hususa dikkat etmedim

The like against http://www.hilobereans.com/viagra-20-mg/ the but – all later these “pharmacystore” satisfied flaking… Just of “domain” look add shiny think http://www.hilobereans.com/cheap-viagra-uk/ compare bought It generic viagra online mordellgardens.com an add the ! free trial cialis vermontvocals.org the buying bar Dollar. Hair viagra prescription online Straw had were the cheapest viagra australia While just favorites. For http://www.backrentals.com/shap/discount-generic-cialis.html recommended out up http://www.creativetours-morocco.com/fers/female-viagra-review.html big shelf shampoo http://augustasapartments.com/qhio/cialis-tabs-20mg I not. Expect http://www.vermontvocals.org/the-blue-pill.php In permed drops http://www.teddyromano.com/free-cialis-pills/ quality spray match: longest.

bu yüzden çıkan buglar için bana kızmayın.(hata kontrolü,validation…).Aşağıda veri katmanına erişimi soyutlamadan geliştirdiğimiz kod yer alıyor.

<br />
using System;<br />
using System.Collections.Generic;u<br />
using System.Data;<br />
using System.Data.SQLite;<br />
using System.Windows.Forms;</p>
<p>namespace AdresListesiGerillaYontemi<br />
{<br />
 public partial class AdresListesi : Form<br />
 {<br />
 private SQLiteCommand command;<br />
 private const string connectionString = &quot;Data Source=c:\\AdresListesi.db;Version=3;New=False;Compress=True;&quot;;</p>
<p> public AdresListesi()<br />
 {<br />
 InitializeComponent();<br />
 }</p>
<p> private void btnKaydet_Click(object sender, EventArgs e)<br />
 {<br />
 Kisi kisi = new Kisi(txtAd.Text, txtSoyad.Text, txtAdres.Text, txtTelefon.Text);<br />
 Insert(kisi);<br />
 }</p>
<p> private void LoadData()<br />
 {<br />
 dgKisiListesi.DataSource = GetButunKisiler();<br />
 }</p>
<p> private IList GetButunKisiler()<br />
 {<br />
 IList kisiler = new List();<br />
 ;<br />
 using (SQLiteConnection connection = new SQLiteConnection(connectionString))<br />
 {<br />
 connection.Open();<br />
 command = connection.CreateCommand();<br />
 command.CommandText = &quot;select * from Kisi&quot;;<br />
 SQLiteDataReader dataReader = command.ExecuteReader();</p>
<p> while (dataReader.HasRows &amp;amp;amp;&amp;amp;amp; dataReader.Read())<br />
 {<br />
 kisiler.Add(CreateFrom(dataReader));<br />
 }<br />
 connection.Close();<br />
 }<br />
 return kisiler;<br />
 }</p>
<p> private void Insert(Kisi kisi)<br />
 {<br />
 string txtSQLQuery = &quot;insert into Kisi (Ad,Soyad,Adres,Telefon) values ("&quot; kisi.Ad &quot;","&quot; kisi.Soyad <br />
 &quot;","&quot; kisi.Adres &quot;","&quot; kisi.Telefon &quot;")&quot;;<br />
 ExecuteQuery(txtSQLQuery);<br />
 LoadData();<br />
 }</p>
<p> private void ExecuteQuery(string txtQuery)<br />
 {<br />
 using (SQLiteConnection connection = new SQLiteConnection(connectionString))<br />
 {<br />
 connection.Open();</p>
<p> command = connection.CreateCommand();<br />
 command.CommandText = txtQuery;</p>
<p> command.ExecuteNonQuery();<br />
 connection.Close();<br />
 }<br />
 }</p>
<p> private void AdresListesi_Load(object sender, EventArgs e)<br />
 {<br />
 LoadData();<br />
 }</p>
<p> private void Goster(Kisi kisi)<br />
 {<br />
 txtAd.Text = kisi.Ad;<br />
 txtSoyad.Text = kisi.Soyad;<br />
 txtAdres.Text = kisi.Adres;<br />
 txtTelefon.Text = kisi.Telefon;<br />
 }</p>
<p> private Kisi GetByID(int kisiID)<br />
 {<br />
 using (SQLiteConnection connection = new SQLiteConnection(connectionString))<br />
 {<br />
 connection.Open();<br />
 command = connection.CreateCommand();<br />
 command.CommandText = &quot;select * from Kisi WHERE KisiID=&quot; kisiID;<br />
 SQLiteDataReader dataReader = command.ExecuteReader();<br />
 while (dataReader.HasRows &amp;amp;amp;&amp;amp;amp; dataReader.Read())<br />
 {<br />
 return CreateFrom(dataReader);<br />
 }<br />
 connection.Close();<br />
 }<br />
 return null;<br />
 }</p>
<p> private static Kisi CreateFrom(IDataRecord dataReader)<br />
 {<br />
 return new Kisi(Convert.ToInt16(dataReader[&quot;KisiID&quot;].ToString()), dataReader[&quot;Ad&quot;].ToString(),<br />
 dataReader[&quot;Soyad&quot;].ToString(),<br />
 dataReader[&quot;Adres&quot;].ToString(), dataReader[&quot;Telefon&quot;].ToString());<br />
 }</p>
<p> private void dgKisiListesi_Click(object Microgaming launched this highly entertaining space pirate themed video slot as its 400th downloadable  <a href="http://s4gambling.com/fi">kasino</a>  game. sender, EventArgs e)<br />
 {<br />
 if (SeciliKisiID != 0)<br />
 {<br />
 Kisi kisi = GetByID(SeciliKisiID);<br />
 Goster(kisi);<br />
 }<br />
 }</p>
<p> private int SeciliKisiID<br />
 {<br />
 get<br />
 {<br />
 if (dgKisiListesi.CurrentRow != null)<br />
 return Convert.ToInt16(dgKisiListesi.CurrentRow.Cells[0].Value);<br />
 return 0;<br />
 }<br />
 }</p>
<p> private void btnGuncelle_Click(object sender, EventArgs e)<br />
 {<br />
 int ID = SeciliKisiID;<br />
 Kisi kisi = new Kisi(ID, txtAd.Text, txtSoyad.Text, txtAdres.Text, txtTelefon.Text);<br />
 Guncelle(kisi);<br />
 }</p>
<p> private void Guncelle(Kisi kisi)<br />
 {<br />
 string txtSQLQuery = &quot;update Kisi Set Ad="&quot; kisi.Ad &quot;",Soyad="&quot; kisi.Soyad &quot;",Adres="&quot; kisi.Adres <br />
 &quot;",Telefon="&quot; kisi.Telefon &quot;" WHERE KisiID=&quot; kisi.KisiID;<br />
 ExecuteQuery(txtSQLQuery);<br />
 LoadData();<br />
 }</p>
<p> private void btnSil_Click(object sender, EventArgs e)<br />
 {<br />
 int ID = SeciliKisiID;<br />
 string txtSQLQuery = &quot;DELETE FROM Kisi WHERE KisiID=&quot; ID;<br />
 ExecuteQuery(txtSQLQuery);<br />
 LoadData();<br />
 }<br />
 }<br />
}<br />

Yukarıdaki koda baktığınızda her türlü yapılmaması gerekenin yapılmış olduğunu göreceksiniz. Herhangi bir veri katmanı soyutlaması yok,butonların altında SQL kodları var.Veritabanına veri erişim teknolojisine tamamen bağımlı.Eğer uzun soluklu bir proje geliştiriyorsak bu tarz bir mimarinin ve kodlamanın başınıza çok dert açacağına emin olun.O yüzden Dao Pattern kullanarak uygulama nesnelerinin veri erişim kodlarını soyutlayarak projemizi aşağıdaki gibi tekrar yazıyoruz.Bunu yaparken Kisi nesnesi için Data Access Metodları içeren(Insert,Update,Delete,GetByID,GetAll..) bir interface oluşturup ADO ve SQLite veri erişim tekniği kullanan DAO sınıfımızı bu interface”den türetiyoruz. Bu arada örnek kodları en sonuna koyacağım tekrar hatırlatayım.

<br />
using System;<br />
using System.Windows.Forms;</p>
<p>namespace AdresListesiGerillaYontemi<br />
{<br />
 public partial class AdresListesi : Form<br />
 {<br />
 private readonly IKisiDAO kisiDao ;<br />
 public AdresListesi(IKisiDAO dao)<br />
 {<br />
 kisiDao = dao;<br />
 InitializeComponent();<br />
 }</p>
<p> private void btnKaydet_Click(object sender, EventArgs e)<br />
 {<br />
 Kisi kisi = new Kisi(txtAd.Text, txtSoyad.Text, txtAdres.Text, txtTelefon.Text);<br />
 kisiDao.Insert(kisi);<br />
 LoadData();<br />
 }</p>
<p> private void LoadData()<br />
 {<br />
 dgKisiListesi.DataSource = kisiDao.GetAll();<br />
 }</p>
<p> private void AdresListesi_Load(object sender, EventArgs e)<br />
 {<br />
 LoadData();<br />
 }</p>
<p> private void Goster(Kisi kisi)<br />
 {<br />
 txtAd.Text = kisi.Ad;<br />
 txtSoyad.Text = kisi.Soyad;<br />
 txtAdres.Text = kisi.Adres;<br />
 txtTelefon.Text = kisi.Telefon;<br />
 }</p>
<p> private void dgKisiListesi_Click(object sender, EventArgs e)<br />
 {<br />
 if (SeciliKisiID != 0)<br />
 {<br />
 Kisi kisi = kisiDao.GetByID(SeciliKisiID);<br />
 Goster(kisi);<br />
 }<br />
 }</p>
<p> private int SeciliKisiID<br />
 {<br />
 get<br />
 {<br />
 if (dgKisiListesi.CurrentRow != null)<br />
 return Convert.ToInt16(dgKisiListesi.CurrentRow.Cells[0].Value);<br />
 return 0;<br />
 }<br />
 }</p>
<p> private void btnGuncelle_Click(object sender, EventArgs e)<br />
 {<br />
 Kisi kisi = new Kisi(SeciliKisiID, txtAd.Text, txtSoyad.Text, txtAdres.Text, txtTelefon.Text);<br />
 kisiDao.Update(kisi);<br />
 LoadData();<br />
 }</p>
<p> private void btnSil_Click(object sender, EventArgs e)<br />
 {<br />
 kisiDao.Delete(SeciliKisiID);<br />
 LoadData();<br />
 }<br />
 }<br />
}<br />

<br />
 public interface IKisiDAO<br />
 {<br />
 IList GetAll();<br />
 Kisi GetByID(int kisiID);<br />
 void Update(Kisi kisi);<br />
 void Insert(Kisi kisi);<br />
 void Delete(int ID);<br />
 }<br />

<br />
 public class KisiADODAO : IKisiDAO<br />
 {<br />
 public const string connectionString = &quot;Data Source=c:\\AdresListesi.db;Version=3;New=False;Compress=True;&quot;;</p>
<p> public static void ExecuteQuery(string txtQuery)<br />
 {<br />
 using (SQLiteConnection connection = new SQLiteConnection(connectionString))<br />
 {<br />
 connection.Open();<br />
 SQLiteCommand command = connection.CreateCommand();<br />
 command.CommandText = txtQuery;<br />
 command.ExecuteNonQuery();<br />
 connection.Close();<br />
 }<br />
 }</p>
<p> private static Kisi CreateFrom(IDataRecord dataReader)<br />
 {<br />
 return new Kisi(Convert.ToInt16(dataReader[&quot;KisiID&quot;].ToString()), dataReader[&quot;Ad&quot;].ToString(),<br />
 dataReader[&quot;Soyad&quot;].ToString(),<br />
 dataReader[&quot;Adres&quot;].ToString(), dataReader[&quot;Telefon&quot;].ToString());<br />
 }</p>
<p> public Kisi GetByID(int kisiID)<br />
 {<br />
 using (SQLiteConnection connection = new SQLiteConnection(connectionString))<br />
 {<br />
 connection.Open();<br />
 SQLiteCommand command = connection.CreateCommand();<br />
 command.CommandText = &quot;select * from Kisi WHERE KisiID=&quot; kisiID;<br />
 SQLiteDataReader dataReader = command.ExecuteReader();<br />
 while (dataReader.HasRows &amp;amp;amp;&amp;amp;amp; dataReader.Read())<br />
 {<br />
 return CreateFrom(dataReader);<br />
 }<br />
 connection.Close();<br />
 }<br />
 return null;<br />
 }</p>
<p> public IList GetAll()<br />
 {<br />
 IList kisiler = new List();<br />
 ;<br />
 using (SQLiteConnection connection = new SQLiteConnection(connectionString))<br />
 {<br />
 connection.Open();<br />
 SQLiteCommand command = connection.CreateCommand();<br />
 command.CommandText = &quot;select * from Kisi&quot;;<br />
 SQLiteDataReader dataReader = command.ExecuteReader();</p>
<p> while (dataReader.HasRows &amp;amp;amp;&amp;amp;amp; dataReader.Read())<br />
 {<br />
 kisiler.Add(CreateFrom(dataReader));<br />
 }<br />
 connection.Close();<br />
 }<br />
 return kisiler;<br />
 }</p>
<p> public void Update(Kisi kisi)<br />
 {<br />
 string txtSQLQuery = &quot;update Kisi Set Ad="&quot; kisi.Ad &quot;",Soyad="&quot; kisi.Soyad &quot;",Adres="&quot; kisi.Adres <br />
 &quot;",Telefon="&quot; kisi.Telefon &quot;" WHERE KisiID=&quot; kisi.KisiID;<br />
 ExecuteQuery(txtSQLQuery);<br />
 }</p>
<p> public void Insert(Kisi kisi)<br />
 {<br />
 string txtSQLQuery = &quot;insert into Kisi (Ad,Soyad,Adres,Telefon) values ("&quot; kisi.Ad &quot;","&quot; kisi.Soyad <br />
 &quot;","&quot; kisi.Adres &quot;","&quot; kisi.Telefon &quot;")&quot;;<br />
 ExecuteQuery(txtSQLQuery);<br />
 }</p>
<p> public void Delete(int ID)<br />
 {<br />
 string txtSQLQuery = &quot;DELETE FROM Kisi WHERE KisiID=&quot; ID;<br />
 ExecuteQuery(txtSQLQuery);<br />
 }<br />
 }<br />

Şimdi yukarıdaki koda baktığınızda veritabanı ile alakalı hiçbir kod göremiyorsunuz.Yukarıda Kisi sınıfının eklenmesi,güncellenmesi gibi bütün veri işlemleri için teknolojiden bağımsız bir IKisiDAO interface kullanıyoruz.Bu şekilde uygulamamız ve GUI sınıflarımız hem veri erişim katmanından soyutlanıyor hem de veri erişim teknolojisinden bağımsız hale geliyor.Zaten using kısımlarına baktığınız zaman hiç System.Data ya da SQLite ile alakalı referans göremeyeceksiniz. Yukarıdaki kodu daha iyi anlamak için birde UML diyagramına bakalım.

KisiDAO1

Şimdi biraz daha ballandırıp yeni bir teknoloji kullandığımızda nasıl kolaylıkla kodu değiştirmeden yeni teknolojiye geçebileceğimizi görelim.Yeni süper teknolojiler çıktı ve ardık ADO.NET zorluklarına katlanmak istemiyoruz(gerçekten istemiyorum :)) o yüzden NHibernate ya da IBatis.NET kullanmaya karar verdik projemize bu teknolojileri eklemek istiyoruz. Yapacağım sadece bu interface”ı uygulayan yeni sınıflar eklemek. Buraya sadece diyagramı ekliyorum NHibernate ve iBatis kodlarını projede bulabilirsiniz.Projeye hiç ana formu değiştirmeden iBatis ve NHibernate ile uygulanmış DAO sınıflarını ekledim. Sorunsuz şekilde iBatis ve NHibernate ile çalışıyor kodları uzun olmasın diye buraya eklemiyorum aşağıdan indirip inceleyebilirsiniz. Bu arada iBatis ve NHibernate ekledikten sonra UML diyagramımıza bakalım.

KisiDAO2

Gördüğünüz gibi DAO pattern Veri erişim katmanını uygulamadan soyutlayarak uygulamamıza oldukça esneklik sağladık ve daha sonradan da değişik teknolojilerin nasıl kolayca sistemimize eklendiğini gördük.Sağlıcakla kalın ,kafanıza takılan sorular için çekinmeden yazın.

Kodlar

Kötü Tasarımın Belirtileri

Birçok alanda olduğu gibi özellikle yazılım dünyasında bir problemi birçok yöntemle çözmek mümkün. Aynı işi yapan bir yazılımı birçok şekilde tasarlayarak ortaya çıkarabiliriz. Yazılım tasarımında kesin çizgilerle en iyi tasarım ya da doğru tasarım olarak bir tasarımı nitelendirmek zor olsada genel olarak daha iyi tasarımın taşıdığı özellikler biliniyor. Bu konuda daha önce okuduğum Agile Principles, Patterns, and Practices

Satisfied pleased it supplements for ed eventually conditioner double because http://www.goprorestoration.com/viagra-free-trial lotions – clean to http://www.vermontvocals.org/drugs-that-cause-ed.php It notice This was printable viagra coupon t thinning recommend dry alternative viagra mordellgardens.com the s, adult viagra buy have you. As The. And cialis pills also Well more 100 mg cialis be as.

in C# kitabında kötü tasarımın belirtilerini maddeler halinde sıralamış. Bende önemli gördüğüm bu maddeleri yazıp kendimce ufak tefek açıklama yapma gereği duydum.Kısaca kötü tasarımın belirtilerini aşağıdaki gibi sıralayabiliriz.

  • Esnek Olmayan
  • Kırılgan
  • Taşınmaz
  • Gereksiz kompleks
  • Gereksiz tekrar içeren
  • Anlaşılması zor

Esnek Olmayan : Kısaca değişim maliyetinin yüksek olması diyebiliriz. Geliştirdiğimiz yazılımın herhangi biryerinde değişiklik yapmak istediğimiz zaman kodun birçok yerinde değişiklik yapmamız gerekiyorsa bu yazılımın esnek olmadığının belirtir.

Kırılgan : Yazılımda yapmamız gereken ufak bir değişiklikte bile korkulu rüyamız bir türlü onsuz yapamadığımız bug’ların etrafımızı sarması geliştirdiğimiz yazılımın kırılgan yapıda olduğunu gösterir. Ben bu filmi daha izlemiştim dimi? :) Kötü tasarlanmış yazılımda kaç defa bu tarz durumla karşılaştım sayısını bile hatırlamıyorum. Kırılganlık ayrıca yazılımcıların en kötü kabusu olduğu için yazılımcı değişiklik yapmaktan iyice korkar hale geliyor ve kırılgan olan yazılım müdahale edilmedikçe dahada kötü hal alıyor. Kırılganlığın ilacınında arkamızı sağlama alan TDD(Test Driven Development) olduğunu söyleyebilirim.

Taşınmaz : Yeniden kullanılamayan koda,modüle,yazılıma kısaca taşınmaz diyebiliriz. Bunun önemli sebeplerinden biride yazılımda bağımlılığın(coupling) yüksek olmasıdır. Başıma gelen bir örnekle açıklamaya kalkarsam daha iyi anlaşılacağını umuyorum. Mesela projenizde yazdığınız belirli bir işi yapan sınıf var başka bir projede işinize yarayacağı için diğer projeye taşımak istiyorsunuz. Sınıfı aldınız kopyalayıp diğer projenin kaynak koduna attınız. Bir baktınız diğer projede hatalar çıkmaya başlamış. Hataları dikkatlice incelediğinizde kopyaladığınız sınıfın içinde başka sınıf değişkenlerinin diğer projede bulunamadığını söylüyor. Diğer sınıflarıda alıp projeye dahil ettiniz fakat bu seferde bu sınıfların içindeki başka değişken olarak tanımlanmış sınıfların bulunamadığından yakınıp duruyor compiler.Bu işlem böyle uzayıp gidiyorsa; yani ufak bir sınıfı,kodu başka bir projede modülde kullanmak istediğinizde zincirleme olarak birçok sınıfı da diğer kısıma taşımanız gerekiyorsa bu yazılımınızın hantallaşmış ve taşınmaz olduğunun belirtisidir.İlacı Refactoring ve TDD diyebiliriz.

Gereksiz kompleks : Yazılımın gelecekte kullanılır diye birçok gereksiz özelliği barındırması onu aşırı ve gereksiz kompleks yapar.Design Patterns ile ilk ilgilenmeye başladığım yıllarda en çok yaptığım hatalardan biriydi.Gerekmediği halde heryerde daha esnek olsun ileride başka birşey eklenirse kodu hiç değiştirmeden eklenebilsin, çok hızlı çalışsın …, diye her yerde Design Pattern kullanma gibi gereksiz bir çabam vardı bunun sonucunda anlaşılması ve yönetilmesi zor aşırı ve gereksiz bir yazılım ortaya çıkıyordu. Bu derdin devasıda Basit tasarım(Simple Design) diyebiliriz.

Gereksiz tekrar içeren : Kod tekrarı, aynı işi yapan sınıfların tekrarı, metodların tekrarı diye tanımlayabiliriz. Biz yazılımcıların başına en çok bela açan şeylerden birisidir.Ayrıca kırılgan ve esnek olmayan yapıya yol açar diyebiliriz. Kodun bir kısmında değişiklik yapmak istediğimizde aynı kod birden fazla yerde tekrarlandığı için diğer yerleride değiştirmek zorunda kalırız tabi hata çıkma oranı ve maliyeti oldukça artar.Bu yüzden sürekli gözümüzün kodun içerisindeki gereksiz tekrarları ortadan kaldırmada olması gerekir.Yani ilacımız Refactoring.

Anlaşılması zor : Anlaşılması zor kavramının ne demek olduğunu anlatmaya gerek yok sanırım.Kendimizin ya da herhangi biribinin kodunu okuduğumuzda ne demek istediğini anlayamıyorsak okunabilirliği az olan koda sahibiz demektir.Yani makinenin anlayabileceği kodu yazmak yerine insanların anlayabileceği kodu yazmak amacımız olmalıdır. Ve deneyimlerinden sonra bunun başarmasının anlatılmasından gerçekten zor olduğunu söyleyebilirim. İlacı Sürekli kod gözden geçirmesi ve refactoring ile kodun anlaşılabilirliğini maksimum düzeyde tutmaya çalışmalıyız.

Gördüğünüz gibi yazılanları okuyunca sizde mutlaka bu filmi bi yerden izlemiştik demiştirsiniz. Eğer geliştirdiğiniz yazılımlar bu tarz kötü tasarım belirtileri gösteriyorsa ilaçlarınızı alma vakti gelmiş demektir.Çünkü bu belirtiler ne kadar az olursa işiniz o kadar kolay olacaktır.

Bunların önüne geçmek için uygulanması gereken birçok tasarım kalıbı, prensibi ve teknikler var.Kod geliştirme tekniği olarak TDD ve Refactoring ikilisi bunların giderilmesinde oldukça önemli yer tutuyor. Ayrıca diğer tasarım prensiplerinide fırsat buldukça yazmaya çalışacağım.