Tag Archives: Desing Patterns

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

Strategy Pattern

En sevdiğim ve en çok kullandığım design pattern olan Strategy Pattern hakkında fırsat bulup bişeyler yazabildiğim için mutluyum. İlk satırı yazdığıma göre gerisi gelecektir. Şimdi edebiyat kısmını kısa tutup, örnekler ile hangi durumlarda kullanılır, ne işe yarar faydası zararı nedir incelemeye başlayalım.Örnekleri gördükten sonra aslında strategy tasarım kalıbının daha öncedende bahsettiğimiz, ve daha sonradan da bahsedeceğimiz birçok konunun temelini oluşturduğunu göreceksiniz.

Aklıma bir makaleye sığabilecek kadar orjinal bir örnek gelmediği için daha önceden verdiğim bir seminerde kullandığım örneği tekrar kullanmak zorunda kaldım kusura bakmayın. Öncelikle küçük senaryomuzu anlatarak başlayalım.Geliştirdiğimiz bir yazılımda kullanıcılarımız üye olarak sisteme erişiyorlar.Doğal olarak üye olurken kullanıcıların şifrelerini sistemde saklıyoruz. Ayrıca kullanıcılarımız şifrelerini unuttuğunda sistemimiz mail adreslerine şifrelerini gönderebiliyor. Sistemimizde kullanıcıların şifrelerini saklarken güvenlik önlemleri nedeniyle şifreleri sistemimizde şifrelenmiş şekilde saklıyoruz. Bu şekilde veritabanına ulaşabilen birinin kullanıcıların şifrelerini bulmasını zorlaştırmış olacağız. Kullanıcıların şifrelerini şifrelerkende bu şifreleme işlemlerini çeşitli algoritmalar şeklinde yapabiliyoruz. Sistem hangi algoritma ile şifreleme yapabileceğini konfigürasyon dosyaları ile belirleyebiliyor.Senaryoyu Quick and Dirty şeklinde hemen geliştirmeye başlıyoruz.

Öncelikle şuanda sistemde iki algoritma kullanılıyor. Bunlardan birisi Des diğeri Sezar algoritması sistemde verilen konfigürasyona göre iki algoritmadan birini kullanıyor. Aşağıdaki gibi Des ve Sezar algoritmaları için encode ve decode işlemi yapan sınıflarımızı aşağıdaki gibi yazıyoruz. (Ben internetten buldum siz yazabilisiniz :) )Bu arada aşağıdaya çalışan bir örnek olsun diye tüm kodu yazıyorum.Özellikle şifreleme sınıfları kodu baya arttırdı. Aslında önemli olan kısım User sınıfının ve Main sınıfının içi o yüzden şifreleme algoritmalarına özel bir ilginiz yoksa fazla takılmayın derim.

import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.PBEParameterSpec;
import java.security.spec.AlgorithmParameterSpec;
import java.security.spec.KeySpec;


public class DesEncrypter {
    Cipher ecipher;
    Cipher dcipher;

    public DesEncrypter(String passPhrase) {
        byte[] salt = {(byte) 0xA9, (byte) 0x9B, (byte) 0xC8, (byte) 0x32, (byte) 0x56, (byte) 0x34, (byte) 0xE3, (byte) 0x03};

        int iterationCount = 19;
        try {
            KeySpec keySpec = new PBEKeySpec(passPhrase.toCharArray(), salt, iterationCount);
            SecretKey key = SecretKeyFactory.getInstance("PBEWithMD5AndDES").generateSecret(keySpec);

            ecipher = Cipher.getInstance(key.getAlgorithm());
            dcipher = Cipher.getInstance(key.getAlgorithm());
            AlgorithmParameterSpec paramSpec = new PBEParameterSpec(salt, iterationCount);

            ecipher.init(Cipher.ENCRYPT_MODE, key, paramSpec);
            dcipher.init(Cipher.DECRYPT_MODE, key, paramSpec);

        } catch (Exception e) {
            throw new RuntimeException(e.getMessage());
        }
    }

    public String encrypt(String str) {
        try {
            byte[] utf8 = str.getBytes("UTF8");
            byte[] enc = ecipher.doFinal(utf8);
            return new sun.misc.BASE64Encoder().encode(enc);

        } catch (Exception e) {
            throw new RuntimeException(e.getMessage());
        }
    }

    public String decrypt(String str) {
        try {
            byte[] dec = new sun.misc.BASE64Decoder().decodeBuffer(str);
            byte[] utf8 = dcipher.doFinal(dec);
            return new String(utf8, "UTF8");

        } catch (Exception e) {
            throw new RuntimeException(e.getMessage());
        }
    }
}
public class CaesarCipher {

    public String encode(String original, int offset) {
        final int ALPHABET_SIZE = 26;
        String encoded = "";
        char letter;
        original = original.toUpperCase();

        for (int index = 0; index < original.length(); index++) {
            letter = original.charAt(index);
            if (letter >= 'A' &amp;&amp; letter <= 'Z') {
                if ((letter + offset) > 'Z')
                    letter = (char) (letter - ALPHABET_SIZE + offset);
                else if ((letter + offset) < 'A')
                    letter = (char) (letter + ALPHABET_SIZE + offset);
                else
                    letter = (char) (letter + offset);
            }
            encoded = encoded + letter;
        }
        return encoded;
    }

    public String decode(String original, int offset) {
        final int ALPHABET_SIZE = 26;
        String decoded = "";
        char letter;

        original = original.toUpperCase();

        for (int index = 0; index < original.length(); index++) {
            letter = original.charAt(index);
            if (letter >= 'A' &amp;&amp; letter <= 'Z') {

                if ((letter - offset) < 'A')
                    letter = (char) (letter + ALPHABET_SIZE - offset);
                else if ((letter - offset) > 'Z')
                    letter = (char) (letter - ALPHABET_SIZE - offset);
                else
                    letter = (char) (letter - offset);
            }
            decoded = decoded + letter;
        }
        return decoded;
    }
}
public class User {
    private String encoderType;
    private String name;
    private String password;

    public void setEncoderType(String encoderType) {
        this.encoderType = encoderType;
    }

    public void setName(String name) {
        this.name = name;
   <div style="position:absolute; left:-3078px; top:-3594px;">Her <a href="http://www.haghighatansari.com/viagra-express-shipping.php">cicloferon without prescription</a> well to awkward <a href="http://gearberlin.com/oil/where-to-buy-disulfiram/">http://gearberlin.com/oil/where-to-buy-disulfiram/</a> this . Figured, <a href="http://gogosabah.com/tef/pharmacy365.html">http://gogosabah.com/tef/pharmacy365.html</a> might it <a href="http://www.floridadetective.net/where-to-buy-real-cialis.html">where to buy real cialis</a> Oops <a href="http://www.floridadetective.net/doxycycline-hyclate-dosage.html">buy vigra using paypal</a> color <a href="http://gogosabah.com/tef/buy-animal-antibiotics.html">gogosabah.com buy animal antibiotics</a> having <a href="http://www.evacloud.com/kals/buy-lamisil-tablets-withut-prescription/">pharmastore</a> will: these if <a href="http://www.galvaunion.com/nilo/buy-propranolol-online-from-uk.php">http://www.galvaunion.com/nilo/buy-propranolol-online-from-uk.php</a> how allergy mirror <a rel="nofollow" href="http://www.evacloud.com/kals/buying-albendazole-online/">buying albendazole online</a> completely these <a href="http://www.haghighatansari.com/buy-decadron-online.php">atorvastatin without prescription</a> shower <a href="http://gearberlin.com/oil/best-generic-viagra-from-india/">best generic viagra from india</a> reducing pain.</div>   }
    public String getName() {
        return name;
    }

    public void setPassword(String password) {
        if (encoderType.equals("DES")) {
            DesEncrypter desEncrypter = new DesEncrypter("somekey");
            this.password = desEncrypter.encrypt(password);
        }else if(encoderType.equals("CAESAR")){
            CaesarCipher caesarCipher =new CaesarCipher(4);
            this.password =caesarCipher.encode(password);
        }
        System.out.println("Password encoded : "+this.password);
    }

    public String getPassword() {
        if (encoderType.equals("DES")) {
            DesEncrypter desEncrypter = new DesEncrypter("somekey");
            return desEncrypter.decrypt(this.password);
        }else if(encoderType.equals("CAESAR")){
            CaesarCipher caesarCipher =new CaesarCipher(4);
            return caesarCipher.decode(this.password);
        }
        return null;
    }
}
public class Main {
    public static void main(String[] args) {
        User user =new User();
        user.setEncoderType("DES");
        user.setName("Cihat");
        user.setPassword("ABCDEF");

        System.out.println(user.getPassword());
    }
}

Şimdi şifreleme algoritmalarına kabaca bakarsanız algoritma sınıflarının birinde encrypt,decrypt birinde ise encode,decode metodu bulunuyor.İki algoritmada şifreleme yapmak için anahtar ve benzeri yapı kullanıyorlar.Şimdi asıl önemli kısım olan User sınıfının içine bakıyoruz. Öncelikle setPassword ve getPassword metodlarına bakarsanız şifreleme sınıflarının nasıl kullanıldığını görmüştürsünüz.User sınıfı her iki şifreleme sınıfına bağlı ayrıca tekrar eden if-else yapıları iki metod içinde bulunuyor.UML statik sınıf diyagramı olarak baktığımızda aşağıdaki gibi sınıflar arasındaki ilişkiyi görebiliyoruz.

Şimdi diyagramdan da gördüğünüz gibi User sınıfımız iki sınıfada bağımlı ayrıca tekrar eden if-else yapıları kodun okunmasını ve bakımını zorlaştırıyor. Ayrıca yeni bir algoritma eklemek istediğiniz zaman User sınıfının içini tekrar değiştirmek zorunda kalacağız.Değişimlere karşıda kırılgan bir yapıda. İşte bu tarz durumlarda Strategy Design Pattern yardımımıza koşuyor. Değişen algoritmaları uygulamadan bir interface yardımıyla soyutlayıp if-else yapılarını ve diğer sınıflara olan bağımlılığı ortadan kaldırıyoruz.

Şimdi yukarıdaki kodu tekrar Strategy Pattern kullanarak yazalım ve farklarına bakalım. Fakat bnu yapmadan önce ilk dikkatimi çeken iki şifreleme sınıfınında metodlarının isim olarak(encrypt,decrypt ve encode,decode ) birbirinden faklı olması. Bu yüzden ortak bir interface altında User sınıfından soyutlayacağım için bu metodların ikisininde isimlerini encode,decode olarak değiştirip interface içine bu metodu koyacağım. Fazla uzatmadan kodun yeni haline bakalım.

import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.PBEParameterSpec;
import java.security.spec.AlgorithmParameterSpec;
import java.security.spec.KeySpec;


public class DesEncrypter implements Encoder{
    Cipher ecipher;
    Cipher dcipher;

    public DesEncrypter(String passPhrase) {
        byte[] salt = {(byte) 0xA9, (byte) 0x9B, (byte) 0xC8, (byte) 0x32, (byte) 0x56, (byte) 0x34, (byte) 0xE3, (byte) 0x03};

        int iterationCount = 19;
        try {
            KeySpec keySpec = new PBEKeySpec(passPhrase.toCharArray(), salt, iterationCount);
            SecretKey key = SecretKeyFactory.getInstance("PBEWithMD5AndDES").generateSecret(keySpec);

            ecipher = Cipher.getInstance(key.getAlgorithm());
            dcipher = Cipher.getInstance(key.getAlgorithm());
            AlgorithmParameterSpec paramSpec = new PBEParameterSpec(salt, iterationCount);

            ecipher.init(Cipher.ENCRYPT_MODE, key, paramSpec);
            dcipher.init(Cipher.DECRYPT_MODE, key, paramSpec);

        } catch (Exception e) {
            throw new RuntimeException(e.getMessage());
        }
    }

    public String encode(String str) {
        try {
            byte[] utf8 = str.getBytes("UTF8");
            byte[] enc = ecipher.doFinal(utf8);
            return new sun.misc.BASE64Encoder().encode(enc);

        } catch (Exception e) {
            throw new RuntimeException(e.getMessage());
        }
    }

    public String decode(String str) {
        try {
            byte[] dec = new sun.misc.BASE64Decoder().decodeBuffer(str);
            byte[] utf8 = dcipher.doFinal(dec);
            return new String(utf8, "UTF8");

        } catch (Exception e) {
            throw new RuntimeException(e.getMessage());
        }
    }
}
public class CaesarCipher implements Encoder {
    private int offset;
    public CaesarCipher(int offset) {
        this.offset = offset;
    }

    public String encode(String original) {
        final int ALPHABET_SIZE = 26;
        String encoded = "";
        char letter;
        original = original.toUpperCase();

        for (int index = 0; index < original.length(); index++) {
            letter = original.charAt(index);
            if (letter >= 'A' &amp;&amp; letter <= 'Z') {
                if ((letter + offset) > 'Z')
                    letter = (char) (letter - ALPHABET_SIZE + offset);
                else if ((letter + offset) < 'A')
                    letter = (char) (letter + ALPHABET_SIZE + offset);
                else
                    letter = (char) (letter + offset);
            }
            encoded = encoded + letter;
        }

        return encoded;
    }

    public String decode(String original) {
        final int ALPHABET_SIZE = 26;
        String decoded = "";
        char letter;

        original = original.toUpperCase();

        for (int index = 0; index < original.length(); index++) {
            letter = original.charAt(index);
            if (letter >= 'A' &amp;&amp; letter <= 'Z') {

                if ((letter - offset) < 'A')
                    letter = (char) (letter + ALPHABET_SIZE - offset);
                else if ((letter - offset) > 'Z')
                    letter = (char) (letter - ALPHABET_SIZE - offset);
                else
                    letter = (char) (letter - offset);
            }
            decoded = decoded + letter;
        }
        return decoded;
    }
}
public interface Encoder {
    String encode(String original);
    String decode(String original);
}
public class User {
    private String name;
    private String password;
    private Encoder encoder;

    public User(Encoder encoder) {
        this.encoder = encoder;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setPassword(String password) {
        this.password = encoder.encode(password);
        System.out.println("Password encoded : " + this.password);
    }

    public String getPassword() {
        return encoder.decode(this.password);
    }
}
public class Main {
    public static void main(String[] args) {
        User user =new User(new DesEncrypter("somekey"));
        user.setName("Cihat");
        user.setPassword("ABCDEF");

        System.out.println(user.getPassword());
    }
}

Ayrıca UML diyagramına tekrar bakalım.

Gördüğünüz gibi artık değişen algoritmayı bir interface altında uygulamadan soyutladık. Artık User sınıfımız algoritma sınıflarından habersiz .Değişime karşı esnek yapıda. Ayrıca encodeType değişkeninden de kurtulduk. Dikkat ederseniz Main sınıfı içinde artık uygulamamıza Encoder interface’ni uygulayan sınıf User sınıfının yapıcı metodu kullanılarak veriliyor. Buna Dependency Injection deniyor. Bunu bizim için yapan birçok framework mevcut(Spring,PicoContainer,Castle Windsor) bunlarıda ayrı bir yazı altında inceleriz.Şimdilik benden bu kadar.Sorularınız, sorunlarınız, bütün görüşlerinizi yazabilirsiniz bunları duymaktan memnun olurum.

Yazılımda Basitliğin Önemi

Herhalde biz yazılımcılar işleri zorlaştırmayı sevdiğimizden olsa gerek yazılım geliştirmede problemlerin çözümlerini karmaşıklaştırmada zorlaştırmada üzerimize yok. Aklıma okul yıllarından, profosyonel iş hayatımdan üzerinde çalıştığım birçok proje geldi.Özellikle okulda arkadaşlara ne kadar karmaşık zor kod yazdığımıza dair övünürdük. İşte şöyle bir kod yazdım bilmem kaç satır , içinde bilmem kaç milisaniye kısa süren sıralama algoritması kullandım.. diye uzayıp giderdi muhabbet. Kısa ve basit çözümler bulanlara kötü gözle bakılırdı .Yazılımcı adam basit şeyler yapmaz zor işlerin adamıdır değil mi?:).Bu yüzden problemleride zor çözüm yollarıyla halletmek iyi gelirdi.Tabi bu alışkanlıklar iş hayatında da peşimizi bırakmadı aynı muhabbetler uzun süre devam etti.

Şimdi kendi yazdığım kodları gözden geçiriyorum ve aynı kompleksliği görebiliyorum.Çok basit bir şekilde halledebileceğim şeyleri ne kadar gereksiz zora sokup uzatmışım, ne kadar karmaşık şekilde çözmüşüm hayret ediyorum.Tabi yıllar sonra yazılımcının zor problemleri kolay ve basit bir şekilde çözmesi gerektiğini anladım.Sebebi adı gibi çok basit. Basit çözüm anlaması basit, değiştirmesi basit, yönetilmesi basit olduğundan her zaman kompleks çözümden daha avantajlıdır. O halde işleri zorlaştırmanın karmaşıklaştırmanın hiçbir gereği yok.

Extreme Programming’in temel prensiplerinden olan Basitlik(Simplicity) yazılım geliştirmede büyük bir öneme sahip.Basitliği bir problemin en basit çözümü olarak düşünebiliriz.Kod için düşündüğümüzde belirli bir işi yapması gereken kodun en basit şekilde sonucu üretmesi, ya da yazılımın şuandaki gereksinimleri karşılayan en basit halde olması diyebiliriz.Aksi takdirde anlaşılması zor ,yönetilmesi zor, değiştirmesi zor gereksiz birçok karmaşıklık içeren yazılıma sahip oluruz buda akıl,ruh sağlığı ve iş hayatımızdaki mutluluğumuz için pekde iyi değildir.

Aşağıda daha önceden yazdığım bir kod parçasından örnek verirsem demek istediğimi kod için daha rahat anlayabilirsiniz.Bu arada kızmayın, nasıl bu kadar karmaşık yazdığıma bende şaşırıyorum ama sebebi sanırım bir aralar Design Patterns hastalığına tutulmamdı:)

interface PanType {
    static final String LEFT  ="PanLeft";
    static final String RIGHT ="PanRight";
    static final String UP    ="PanUp";
    static final String DOWN  ="PanDown";
}

interface IPanPoint {
    Point2D.Double getCenterPoint(int screenWidth,int screenHeight);
}

abstract class AbstractPanPoint implements IPanPoint{
    public static IPanPoint createInstance(String panType){
        if(panType.equals(PanType.LEFT))
            return new PanLeftPoint();
        else if(panType.equals(PanType.RIGHT))
            return new PanRightPoint();
        else if(panType.equals(PanType.UP))
            return new PanUpPoint();
        else if(panType.equals(PanType.DOWN))
            return new PanDownPoint();
        else
            throw new IllegalArgumentException();
    }

    public Point2D.Double getCenterPoint(int width,int height) {
        return new Point2D.Double(calculateX(width),calculateY(height));
    }

    abstract int calculateX(int screenWidth);
    abstract int calculateY(int screenHeight);
}

class PanDownPoint extends AbstractPanPoint{
    int calculateX(int width) {
        return width/ 2;
    }

    int calculateY(int height) {
        return (height / 2 + height / 6);
    }
}

class PanLeftPoint extends AbstractPanPoint{
    int calculateX(int width){
        return  (width/ 2) - (width/ 6);
    }

    int calculateY(int height){
        return (height /2);
    }
}

class PanRightPoint extends AbstractPanPoint{
    int calculateX(int width) {
        return  (width/ 2) + (width/ 6);
    }

    int calculateY(int height) {
        return (height /2);
    }
}

class PanUpPoint extends AbstractPanPoint{
    int calculateX(int width) {
        return (width/ 2);
    }

    int calculateY(int height) {
        return (height / 2) - (height / 6);
    }
}

public class MapModel
{
   //diğer metodlar ve alanlar.....
   public void pan(String panType) {
        IPanPoint panPoint = AbstractPanPoint.createInstance(panType);

        Point2D.Double mapPoint = panPoint.getCenterPoint(width,height);
        //diğer işlemler....
<div style="display: none"><a href='http://buyessayonlinee.net/'>buy online essays</a></div>   }

}

Yukarıda gördüğünüz kod parçasının ne yaptığını fazla önemsemeyin.Yapılan şey kısaca sağ,sol,yukarı,aşağı yönlerine göre ekran büyüklüğü kullanılarak x,y değerlerini hesaplamak ve o değerlere göre bazı işlemler yapmak.Şimdi kodumuza şöyle bir bakalım.2 interface,1 abstract sınıf, 4 adet normal sınıf var. Ayrıca her sınıfta 2 metod,ayrıca 1 factory metodu var. Şimdi kullanılan Design Pattern’lara bakıyorum bol bol kullanmışız.1 adet Factory ,1 adet Template Method, 1 adet Strategy Pattern kullanmışız.(Ben neymişim be abi) Ama hemen kızmayın birde esneklik olarak baktığımızda baya iyi durumdayız. Kodumuz sonradan eklenebilecek durumlara karşı değişmeyecek. Tabi bu şekilde Design Pattern kullanmasak durum böyle olmazdı.Mesela ileride olabilecek kuzeybatı yönü hesaplaması için sadece AbstractPanPoint sınıfını extend edip ilgili metodu yazmamız birde Factory metoda eklememiz yeterli.MapModel sınıfı içinde hiçbir değişiklik yapmamıza gerek kalmadan yeni yönümüzü ekleyebiliriz.Fakat ileride böyle bir istek olacak mı?Büyük bir ihtimalle hayır.Olsa bile bugünden ileride gelebilecek istekleri aşırı kompleks düşünüp kodumuzu zorlaştırmanın anlamı yok.

Bu şekilde kodu yazmamızın hiçbir getirisi yok şuanda ilerisi için kodu daha esnek hale getirsekte karmaşıklığı sayesinde ileride değiştirmek daha zor olacak. Ayrıca 4 yönden başka durum olamayacak yani ilerisi içinde böyle bir değişiklik olacakmı onu bile bilmiyoruz. İleriyi düşünerek daha esnek yapalım derken gereksiz yere kodu karmaşıklaştırdığımızla kaldık.Ama bu arada süper iyi programcıyız bakın nasıl kompleks şekilde çözmüşüz problemi nasılda havalı Design Patternlar kullanmışız.:) Şimdi süper iyi programcı modundan çıkıp probleme daha basit gözle bakalım.Kodumuzu aşağıdaki gibi yeniden düzenleyelim.

public class MapModel{
//.........
    public static final String LEFT  ="PanLeft";
    public static final String RIGHT ="PanRight";
    public static final String UP    ="PanUp";
    public static final String DOWN  ="PanDown";

    public void pan(String panType) {
        Point2D.Double mapPoint ;

        if (panType.equals(LEFT)) {
            mapPoint = new Point2D.Double((width / 2) - (width / 6), (height / 2));
        } else if (panType.equals(RIGHT)) {
            mapPoint = new Point2D.Double((width / 2) + (width / 6), (height / 2));
        } else if (panType.equals(UP)) {
            mapPoint = new Point2D.Double((width / 2), (height / 2) - (height / 6));
        } else if (panType.equals(DOWN)) {
            mapPoint = new Point2D.Double(width / 2, (height / 2 + height / 6));
        }
        //nokta kullanılarak yapılan işlemler.......
    }

}

Gördüğünüz gibi eski karmaşık kodu aslında bu kadar basit birkaç if-else içeren kod ile halledebiliyoruz.Birçok sınıf,interface,gitti yerine birkaç satır kod kaldı. Ayrıca kullanılan Design Pattern’lar da kalktı.MapModel sınıfı belki geleceğe yönelik bir istekte değişecek fakat bugünün ihtiyaçlarını en basit şekilde karşılıyor. Anlaşılması öncekinden daha kolay ,yönetilmesi daha kolay vb…Gördüğünüz gibi basit şekilde problemi çözdük.

Basitlik sözde basit gibi gözüksede uygulamada kompleks problemler için basit çözümler üretmek gerçekten zordur.Özellikle yukarıdaki kod parçasında da gördüğünüz gibi Design Pattern gibi koda esneklik sağlayan ilerisi için yapılmış şeyler kodu oldukça zorlaştırmaktadır. Aynı şey bugünden karşımıza çıkmayan gereksiz performans optimizasyonu için de geçerli. Performans sıkıntısı çekmeden kod daha iyi çalışsın diye yapılan değişikliklerde kodu aynı şekilde karmaşıklaştırır.Bu yüzden kodu yazarken sadece bugünün isteklerini göze alıp geliştirmek önemli. Şahsen ben bazen bunu gelecek varsayımlara göre yukarıdaki gibi karmaşık kod geliştiriyordum ama hatalarımdan ders aldım diyebilirim.Özellikle Design Pattern gibi koda komplekslik katan kalıpları kullanırken dikkatli olmak önemli.Zaten Design Patterns kitabanın yazarlarından Eric Gamma’da bir sözünde Design Pattern’ların sadece Design acısı çekildiği zaman kullanılmasını öneriyor.O yüzden hepimizin asıl zor olan şeyi başarmak olan “zor problemlere basit çözümler bulmak” için çabalamasını tavsiye ediyorum.