14 Haziran 2016 Salı

4 - INTERFACE SEGREGATION PRINCIPLE


   Herkese merhaba, bir önceki yazımda Liskov's Subtitution Principle ' den bahsetmiştim. Bugün de SOLID prensiplerinden I 'ya denk gelen Interface Segregation Principle yani Arayüz Ayırım Prensibini anlatıcam. Bu prensip diğer prensiplere göre hem daha basit, hem de anlaşılması daha kolay bir prensiptir. Aslında bir önceki Liskov prensibinde kısmen de olsa anlatmıştım, çok basit manada adından da anlaşılacağı gibi özel görevler yapan classlar için interfacelerin ayrılması konusudur. 
    Interface yani Türkçe adıyla arayüzler, sizin de bildiğiniz gibi bir nevi yapılacak işlerin belirlendiği sınıflar arası anlaşma, sözleşmedir. Aslında sınıflara "REHBERLİK" etmek görevindedir, sadece yol göstermek yapılması gereken zorunlu işleri belirtmek görevi içerir. Haliyle interfaceleri miras alan sınıflar, içindeki tüm methodları implemente etmek zorundadır. Interfacelerin yapı ve görevleri bu yazının konusu değil ama kısaca bahsetmiş olduk. Şimdi konumuzla olan kısmına gelirsek, konu zaten çok basit olduğundan hiç dallanıp budaklandırmadan örneğe geçiyorum. 
    ISP kuralı derki; eğer bir sınıf için kalıtım alınan Interface YETERSİZ yada FAZLA geliyorsa o sınıfı dağıtmak yerine interface yapılarını amaca uygun ayırın. Bu tanıma göre ISP bize farklı işler yapan classlar için farklı interface yapıları kurgulamamızı tavsiye eder. Aslında bu Liskov prensibi ile doğrudan bağlantılı bir prensiptir ki zaten daha önce de belirttiğim üzere SOLID prensipleri bir bütünün parçası gibidir, birbirinden ayrılmazlar ve birine ters gelen kurgu otomatik olarak diğerine de terstir. Şimdi lafı uzatmadan hemen örneğimize geçelim. Bu arada bir önceki yazımda hemen tüm örnekler ISP uyumludur, ISP'ye birer örnektir :)

Örnek - 1 :
 Kuş familyasına ait bir tasarım çıkaralım ve aşağıdaki gibi bir tasarımın yanlış olduğunu görelim.

Interface Sagregation Principle Example



































     Yukarıdaki tasarıma bakarsanız "IKus" interface yapısının bazı classlar için FAZLA geldiği bazıları için YETERSİZ kaldığını görebilirsiniz. Örneğin deve kuşunun göç etme ve uçma gibi 2 özelliği yoktur, aynı şekilde eğer papağan sınıfımız olsaydı "Konuşma" özelliği "IKus" interface yapısında tanımlı olmadığından bu özelliği kullanamayacaktık. Yukarıdaki kod bun sebeplerden dolayı öncelikle Liskov'a uymamaktadır, sonrasında ise ISP'ye zaten uymadığı aşikar. Şimdi kodumuzu SOLID prensiplerine göre baştan düzenlersek, aşağıdaki gibi ayrı interfaceler olacaktır.

Interface Sagregation Principle Example






















    Bu şekilde bir tasarım SOLID prensiplerinin tamamına uymaktadır. İnterfaceleri ayırmış bulunmakla birlikte artık gereksiz özelliklerin gereksiz sınıflarda boş yere implement edilmesini önlemiş olduk. Üstelik "Göç edebilen kuş aynı zamanda uçabilen kuştur" mantığında interfaceleri birbirinden miras alarak bir nevi code review yaptık ve kodumuzu daha manalı ve kısa bir hale de getirmiş bulunduk.
   ISP'nin bize üstteki örnekten de anlaşılacağı üzere, "Nesneler, Sınıflar ihtiyaç duymadıkları özelliklerin olduğu Interface'lere bağlı kalmak zorunda bırakılmamalıdır" der. Yani bizim sınıfımız oluşturmuş olduğumuz Interface yapısının 1 özelliğini dahi kullanmıyorsa, hem Liskov hem de ISP' ye göre yanlış yoldayız demektir.

Örnek - 2 :
  Şimdiki örneğimizde de bir e-ticaret sitesini düşünelim. Sitemizde doğal olarak kullanıcılarımız olacak ve bu kullanıcıların rol görev ve yetkileri doğal olarak birbirinden farklı olması gerekiyor. Örneğin bir "Admin" grubumuz olsun ve bu gruba ait üyelerin "Ekleme,Silme,Düzeltme"  gibi işlevleri, "Base" grubumuzun da sadece "Ekleme" işlevi olsun. Oluşturulacak kullanıcıların bu gruplara atanması ve rol görev dağılımlarını gösteren tasarımı direk oluşturacak olursak aşağıdaki gibi olacaktır.(burada hatalı tasarımı vermeye gerek görmedim)

Interface Sagregation Principle Example
































  Görüldüğü üzere A müşterisi "Admin" grubunda yer aldığından tüm özellikleri kapsıyor, B Müşterisi ise sadece ekleme işlevini yerine getirebiliyor.
   Böylelikle SOLID'in I'sına denk gelen Arayüz Ayrımı Prensibinin de sonuna gelmiş bulunuyoruz. Bu prensip Liskov prensibi ile iç içe geçen basit ama çok islevsel bir prensiptir. Konu gayet zevkli ve basit olduğundan çok rahat bir şekilde anlaşıldığını düşünüyorum. Bir sonraki yazım olan SOLID'in D'sine denk gelen DIP prensibinde görüşmek üzere..Saygılarla..

13 Haziran 2016 Pazartesi

3 - LISKOV'S SUBTITUTION PRINCIPLE (LSP) (Liskovun Yer Değiştirme Prensibi)

     Merhaba bir önceki yazımda SOLID Prensiplerinden O 'ya denk gelen Open/Closed Principle' ı anlatmıştım. Şimdi de L 'ye denk gelen Liskov's Subtitution Principle yani Liskov'un Yerine Geçme Prensibinden bahsediyor olacağım. Bu prensibi aslında OCP ile hemen hemen aynı kapıya çıkan, fakat biraz daha özelleşmişi olarak düşünebilirsiniz. 1987-88 yıllarında Barbara Liskov'un dediği cümleye bakacak olursak ;

"Let q(x) be a property provable about objects x of type T. Then q(y) should be provable for objects y of type S where S is a subtype of T."

 Görüldüğü üzere ingilizcesi gayet açık. Bide Türkçesini verecek olursak;

“q(x) fonksiyonu T tipindeki x nesnesi için kanıtlanabilirdir. O halde T tipinin alt tipi olan S tipindeki y nesnesi için de q(y) fonksiyonu kanıtlanabilir olmalıdır.”

Barbara hanımefendinin diğer bir tanımında ise şöyle diyor;

"Aranan yer değiştirme özelliği şöyle tanımlanabilir: T cinsinden parametre alan tüm programlar (fonksiyonlar) P olacak şekilde, S tipinde O1 nesnesi ve T tipinde O2 nesnesi olsun. Eğer O1 ile O2 nesneleri yer değiştirdiğinde P’nin davranışı değişmiyorsa S tipi T tipinin alt tipidir!"

Bu tanım daha bi basit oldu sanki, anladığınızı umuyorum..Tabi ki anlamadınız :) Her iki tanımdan da hiç bir şey anlaşılmayacağı aşikar. Ama üzülmeye gerek yok bunu ilk okuyan kimse anlamıyor zaten emin olabilirsiniz. Konu yıllarca "Acaba ablamız ne demek istiyor?" lakırdıları eşliğinde anlaşılmadığı için, en sonunda bu anlamsızlığa dayanamayan Robert Martin amcamız;

"Subtypes must be substitutable for their base types." 

diyerek son noktayı koyar. Nispeten daha anlaşılır bir kavram olan bu sözün meali ise;

"Alt sınıflardan oluşan nesnelerin, üst sınıfla yer değiştirdiği zaman aynı davranışları göstermek zorundadırlar"

şeklindedir. Biz bunu biraz daha açıp, biraz daha bizden hale getirirsek üst sınıf ve ondan türeyen alt sınıfların birbiriyle birebir uyumlu olması gerekir diyebiliriz. Üst sınıfta olup da alt sınıfın kullanmayacağı bir özellik, method, fonksiyon vb. şeyler olmamalıdır. Bu olduğu takdirde alt sınıfın kullanmadığı özellikler için ekstra kodlar ve kontroller yazılması gerekir ki bu hem OCP hem de Liskov'a ters düşer. Tanımlardan da anlaşılacağı gibi aslında OCP ve Liskov pensipleri iç içe geçmiş durumdadırlar. Liskov prensibine ters olan bir durum otomatik olarak OCP 'ye de terstir denilebilir.Durumu basit ama etkili bir örnekle anlatmaya çalışalım.

Örnek - 1:
  Uçak filomuz olduğunu düşünüyoruz ve bu filo da her tip uçağın yer aldığını göz önüne bulunduralım. Öncelikle hatalı tasarımı çıkarmamız gerekirse;
























    
      IUcak interface'i yazılmış ve tüm uçaklar bu interface üzerinden türetilmiştir. Fakat sorun şudur ki, interface deki methodlar zorunlu olarak implemente edilmek durumunda kalmışlardır. Genele bakıldığı zaman savaş uçağının bombalama fonksiyonu olabilir, ama yolcu uçağının böyle bir fonksiyonu yoktur. Bu durumda ana programda "Bombala()" methodu yazılırken ekstra kontrol yapılmak zorunda kalınmıştır. Bu yapı OCP 'ye zaten uymadığı gibi Liskova da uymaz. Liskova uymama sebebi, üst sınıfın her özelliğinin alt sınıfta yer almamasıdır, yolcu uçağının bombalama fonksiyonu yoktur. Düşünün ki yolcu uçağına Bombala butonu koydunuz ve arkasını boş bıraktınız. Düğmeye basınca ya hata alacaksınız, yada hiç bir tepki almayacaksınız ki hata almak istemiyorsanız üstte yapılan gibi taklalar atmak zorunda kalacaksınız. İşte bu Liskov bu tarz Dummy (sahte kod) durumlarına hayır demektedir. Yani buton varsa, işlevi de olmalı :)
    Liskov prensibine göre bu uçakların birbiri yerine kullanılabilmesi gerekmektedir. O halde bir sınıfta yer alan herhangi bir işlev kendisinden kalıtım alınan herhangi bir sınıfta kullanılamıyorsa bu durum Liskov prensibine aykırıdır denilebilir. Bu durumda Interfaceleri ayırmak yöntemiyle Liskova uygun hale getirilir.


































Aslında burada konuların birbiriyle aşırı bağlantılı olduğunu, hatta yer yer iç içe girdiğini farketmişsinizdir, çünkü SOLID'in I'sına denk gelen Interface Segregation Principle kuralını da aslında siz farkına varmadan anlatmış oldum :)

Örnek - 2:
  Şimdi ki örneğimizde de araba üreten fabrikamızın yazılımını kodladığımızı hayal edelim ve arabanın olmazsa olmazı "Calistir()" metodu ve günümüzdeki hemen her araba da olan "KlimaCalistir()" metodunu bir abstract class yazarak tanımlayalım. Önce hatalı kurgudan bahsedersek aşağıdaki gibi olacaktır.























    Arabanın Ferrari, Mustang, Porcshe olduğu durumlarda sıkıntı yok ama, Murat131 yada Tofaş Doğan Slx olduğu durumlarda ne yapalım :) Yukarıdaki gibi bir tasarımda mecburen Tofas sınıfında ilgili metoda "Null" return ettirmek, yada "KlimaCalistir()" metodunu kullanmamak gerekir, ama ya projeyi ilk açan ekip arkadaşımız bu şekilde çağırırsa..İşte büyük projelerde o zaman buyurun cenaze namazına. Bunun önüne geçmenin yolu olarak hemen LSP 'ye uygun hale getirirsek;

























Örnek - 3:
   Aşağıdaki örnekte de bir e-ticaret sitesi için Standart,Gold ve Platinum üyelik tipinde müşteriler olduğunu düşünelim. Bu müşterilerden sadece Gold ve Platinum tipi üye müşterilere hediye gönderilebildiğini varsayan bir tasarımı görebilirsiniz.




   Örnek - 4:
  Son örneğimizde de bağımsız bir DB Erişim katmanı tasarımına yer verelim



     Bunlar verilebilecek örneklerden sadece bir kaç tanesi. Siz de hayal gücünüzü kullanarak bir çok içinden çıkılmaz durumu bu şekilde daha net, daha akıcı hale getirebilirsiniz. Özetle Liskov prensibini uygularsanız ;

1 - Olması gereken kodun, olması gerektiği yerde olmasından emin olursunuz.
2 - Fazla kod yazmamış olursunuz
3 -Grup içinde yazılan kodlarda standartları ekibinizle birlikte yakalamış, hataya mümkün mertebe "Sıfır" tolerans tanımış olursunuz.

Böylelikle SOLID' in L'si olan Liskov prensibini de anlatmış oldum. Bir sonraki yazım olacak olan SOLID'in I'sına denk gelen Interface Segregation Principle 'da görüşmek üzere.

8 Haziran 2016 Çarşamba

2 - OPEN/CLOSED PRINCIPLE (OCP) (Açık Kapalı Prensibi)

  Merhaba daha önceki yazımda SOLID prensiplerinden Single Responsibility Principle konusunu anlatmıştım,bu yazımda da SOLID kısaltmasındaki O 'ya denk gelen Open/Closed Principle - Açık Kapalılık Prensibine değiniyor olacağım. Konuya uzatmadan giriş yapmak gerekirse OCP bize;

"Yazılım varlıkları (Class,Modül, Fonksiyon, Method vb.) gelişime AÇIK, değişime KAPALI olamalıdır!"

diyor. Bu cümlenin orijinal hali ise Bertrand Meyer' in aşağıdaki ifadesidir.

 "software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification"

   Peki yazılım varlıklarının gelişime açık, değişime kapalı olması ne manaya geliyor? Bunu şu şekilde düşünebiliriz, ana görevi yüklenen class,modül,fonksiyon gibi yapıların zaman içerisinde kodsal olarak değiştirilmemesi, fakat yani eklenecek özelliklere de açık halde kalması gerektiğidir. "Hem yeni özellik ekleyip, hem de var olan yapıyı değiştirmeden bu işi nasıl yapabilirim? Sonuçta bir yerlere kod eklemek zorundayım ve yapıda mutlaka kodsal değişiklik yapmam gerekecek!" dediğinizi duyuyorum :) Anlaması da kurgulaması kadar zor bir yapıya daha hoş geldiniz diyorum. 
   OCP 'yi daha iyi anlatmak için, prensibin çıkış amacından bahsetmek gerekiyor galiba. Bu prensip kurgulanan yazılımın "Sürekli Değişime Maruz Kalacağı" düşünülerek ortaya çıkmıştır, nitekim de öyledir. Hiç bir projenin başlangıç aşamasından sonunda kadar olacak süreç belli değildir, hatta öyle projeler olur ki %50-60 oranında değişiklik dahi yapılır. Bunun bir çok sebebi vardır ve bu sebeplere burada girmek istemiyorum aksi halde yazı bitmez :) Bu kadar çok değişikliğin yapıldığı projelerde kodlar sürekli sil baştan yazılmak zorunda kalınmasın diye, varlıkların gelişime açık değişime kapalı olması gerekliliği ortaya çıkmıştır. Unutmayın ki her projede gelişim devamlı olacak, sürekli yeni istekler yeni geliştirmeler olacaktır. Tasarımları yaparken ilerde oluşabilecek yeni özellikleri de düşünerek yapmamız gerekecektir ki doğal olarak gelişime açık olsun.

Örnek - 1 :
 Şimdi e-ticaret yapan bir firmanın fatura hesaplama evresini düşünelim ve fatura dip toplam tutarının hesaplandığı tasarımı çıkaralım.















Futuraya ait toplam tutarın hesaplanmasında problem gözükmüyor. Şimdi bir de sipariş tutarının hesaplandığı ve sipariş tutarında iskonto hesabının yansımadığını düşünelim. 
























   Görüldüğü üzere ana işlevi yerine getiren "TutarHesapla()" fonksiyonunu değiştirmek zorunda kaldık.Hatta ilerde farklı belge tipleri (İrsaliye, Fiş vs) geldikçe de sürekli değiştirmek zorunda kalacağımız bir yapı oldu. Bu tarz bir program bağımlılıklardan dolayı hatalıdır, eklemek istediğimiz her belge tipinde ana fonksiyonu sürekli değiştirmek zorunda kalacağımız aşikar ve bu durumda hem ileride düşeceğimiz sıkıntılar hem de çıkabilecek binbir türlü hata gözünüzün önünde canlanmıştır.
   Şimdi gelelim yapımızı OCP' ye uygun hale getirmeye.Öncelikle ana işlevi yerine getiren metodu değişime kapalı hale getirip, programı da gelişime yeni modüllerin eklenmesine açık hale getirmem gerekiyor. Ayrıca enum tipli yapıdan da sıyrılmam gerekiyor. Peki bunu nasıl yaparım derseniz, işte tam da bu noktada abstract class 'lar devreye giriyor.























     Son yapılan refactoring ile şimdi programın gelişime açık, değişime kapalı olması prensibini gerçeklemiş oldum.Artık tek yapmam gereken "Belge" sınıfından türeyen yeni bir sınıf inşa etmek olacak (gelişime açık), ayrıca "TutarHesapla()" fonksiyonunda artık hiç bir değişiklik yapmak zorunda kalmıyorum (değişime kapalı). Tüm bunlara artı olarak bir önceki yazıda anlatmış olduğum SRP 'ye de uygun olduğunu görebilirsiniz, her sınıfın kendine ait 1 işlevi ve her metodunda 1 görevi vardır. Aslında üstteki yapı Liskov Prensibine de uyuyor, fakat bir sonra ki yazımda o noktaya değineceğim için şuan girmiyorum.

Örnek - 2 :
Bir önceki örnek OCP uygulamanın en basit tekniğinden (abstract class) birisiydi. Şimdi ise biraz daha farklı olarak Interface yapısı ile programı OCP 'ye uygun hale getiririz ona bakalım.Öncelikle hatalı yapıyı kurgularsak, aşağıdaki gibi olacaktır.



Intarface kullanılmasına rağmen "TutarHesapla()" metodunda hem gereksiz yere hemde OCP'ye uymayan tarzda kodlar yazılmıştır. Bu kısım eklenen her belgede yine değiştirilmek zorunda kalacaktır. Şimdi burayı değişime kapalı hale getirip, daha genel bir yapıya aşağıdaki gibi çevirebiliriz. Dip not olarak aslında alttaki örneğin de SRP ve Liskova uyduğunu unutmayınız.

























     SOLID' in  O'su olan OCP'nin olayı aslında temel olarak bu kadar. Yazılımda kodlarla oynanmadan zaten bir geliştirme olamaz, fakat amacımız temel işlevi üzerinde barındıran kodlarda değişiklik yapmadan, belli kısımlarda ekleme yaparak mümkün mertebe çok az kod yazacak şekilde istenen geliştirmeyi tamamlayabilmektir. Böylelikle bu ilkenin temel amacının minimum kod yazma olduğunu belirtebiliriz.
Bir sonraki yazım olacak olan SOLID 'in L'si Liskov Subtutition Principle 'de görüşmek üzere.

7 Haziran 2016 Salı

KISA BİLGİ - STOPWATCH

     Merhaba, kısa bilgi köşemizde C# Stopwatch özelliğinden bahsedicem. Bazen yazdığımız kod bloğunun ne kadar sürede çalıştığını ölçmek isteriz. Gerek performans ölçümü gerekse de programın akışını daha hızlı hale getirmek için bu tip ölçümler zaman zaman gerekmektedir. Aslında yazılan kodun performans problemi ve kod optimizasyon çalışmaları bambaşka bir süreç, bu konuyla ilgili dile bağlı olan kurallar ve ölçümün yapılabileceği external toollar vardır. Bu yazıda bu konulara girmek istemiyorum ama yine de çok kısa olarak, daha profesyonel performans ölçümü yapmak için Jetbrains - dotTrace ürününü kullanabilirsiniz deyip hızlıca geçiyorum.
   Aşağıda görüldüğü üzere çalışma süresini ölçmek istediğim kod bloğunu stopwatch nesnesinin "Start" ve "Stop" komutları arasında alıyorum. Daha sonrasında çıkan sonucu "Elapsed" komutu ile izleyebiliyorum ve ekrana yazdırıyorum.















Ekran Çıktısı :








Görüldüğü gibi for ile döndüğüm kod bloğunu 10 sn kadar uyutmuştum. Sonuç olarak da kod bloğu 10 sn gibi bir sürede çalışmış oldu. Aynı kodu TimeSpan nesnesine çevirip daha hassas sonuçlarda elde edebiliriz. TimeSpan nesnesinden hours,minutes,seconds,miliseconds istediğimiz hassasiyette çalışma sonucunu görebiliriz.


















Sonuç Olarak:

6 Haziran 2016 Pazartesi

1 - SINGLE RESPONSIBILITY PRINCIPLE (SRP) ( Tek Sorumluluk Prensibi)

     Merhabalar, bu yazımda bir önceki yazımda SOLID kısaltmasıyla  bahsetmiş olduğum Tasarım Prensiplerinden, SOLID'in ilk harfi olan S 'yi ifade eden "Single Responsibility Principle" den bahsediyor olacağım. 
      Tek Sorumluluk Prensibi olarak nitelendirilen SRP adından da anlaşılacağı gibi bir sınıf veya metodun tek bir görev/sorumluluğunun olması gerektiğini belirtmektedir. SRP bize açık bir şekilde, bir sınıf veya metodun değiştirilebilmesi için tek bir sebebinin bulunması gerektiğini izah etmektedir. Birden fazla görev veya sorumluluk yerine getiren metod veya sınıfların değiştirilmeye açık birden fazla sebepleri olacak, bu da zamanla kodda karmaşıklık ve olası hata ihtimalini çoğaltacaktır. Teoride bu şekilde açıkladığımız olay, elbette projelerimizde gerek zaman darlığından, gerekse de SRP' ye dikkat edilmediğinden bir metod veya sınıfın birden fazla işi yerine getireceği şekilde dizayn edilmektedir. Bu tasarlayacağımız dizaynda sınıf veya metodların birbirine olan bağlılıkları düşünüldüğünde zaman içerisinde yapılacak herhangi bir fonksiyonel değişikliğin, bağlı diğer yapılardaki fonksiyonellikleri ne derecede etkileyeceğini ön görmemiz neredeyse imkansızdır ve buda hemen hemen tüm sürecin en baştan bütünüyle test edilmesini gerektirecektir. Bunu yapabilecek otomatik test programlarımızın olduğunu varsaysak bile ( ki çoğu yazılım firmasında dahi yok), çıkacak hataları çözmek ve süreci sorunsuz atlatmak için ne kadar iş gücüne ihtiyacımız olduğunu göz önünde bulundurmanız gerekmektedir.
      Durumu örneklendirmemiz gerekirse aşağıdaki gibi hem promosyonları hesaplayan hem de fatura kaydeden bir metodumuz olduğunu düşünelim;









"PromosyonHesaplaFaturaKes" metodunun yaptığı 2 iş olduğundan, bu metodu değiştirmek için 2 farklı ana sebep hatta bunlara bağlı onlarca yan sebep olacaktır. Bu kesinlikle istenmeyen bir durumdur, ayrıca bu metod işlev olarak başka yerlerde kullanılmaya uygun değildir. Promosyon hesapları sürekli değişebileceğinden aynı metod muhtemelen başka hiç bir yerde kullanılamayacaktır. Bu yüzden bunları ayırıp aşağıdaki şekilde 2 parçaya bölmemiz gerekecektir.










Hatta ve hatta yapısal olarak promosyon hesabı sipariş ve irsaliyede de olabileceğinden ve yine promosyon hesaplama işleminin fatura kesme işleminden çok farklı bir işlevi olduğundan, bu metodlar ayrı sınıfların üyesi olarak yazılmalıdırlar. Son hali aşağıdaki gibi olan bir yapıda Promosyon hesaplamada bir hata varsa veya bir değişiklik isteniyorsa nereye bakacağımız bellidir. Tek bir işlevi olan "PromosyonHesapla" metodundaki değişiklik programa dinamizm kazandıracak, ayrıca birden fazla yerde (sipariş, irsaliye,fatura vs) kullanılabilecektir.















          Yazılan ilk hatalı yapıdaki gibi farklı farklı işleri yapan metodları maalesef çoğu zaman aynı sınıfın içine yazıyoruz ( dolduruyoruz desek daha mantıklı). Bu tip durumlarda satır sayısı 5000 'leri bulan sınıflarımız oluyor. Düşünün ki 5000 satırlık bir sınıf, ne işler yapıyordur, neler neler barındırıyordur içinde. Entegrasyonundan tutun, loglamaya hatta sisteme login logout olmaya kadar her işi defalarca yapan sınıflar hayal edebilirsiniz, etmeyin :) Ayrı görevi ve sorumlulukları olan ayrı sınıflar ve metodlar hayal edin, tıpkı bir insanın hem doktor hem mühendis olamayacağını ve bunların ayrı görev sorumlulukları olduğu gibi. Hatta biraz daha irdeleyin doktor ve mühendisi ayırdıktan sonra bunları da kendi içinde ayırma yoluna gidin; göz doktoru, cerrah, kbb doktoru gibi...Unutmayın ki eğer yazdığınız metodlar 30-35 satırı, sınıflar da 1000 satırı geçiyorsa yaptığınız işte bir yanlışlık vardır :) Yazılan kodların mutlaka tekrar gözden geçirilmesi ve mümkün mertebe farklı işler yapan daha küçük parçalara ayrılması gerekmektedir.
     Programı yazarken, tasarlarken mümkün mertebe bu şekilde düşünerek en dibe inmeye çalışın. Ayrı işlevi olan bölümleri kesinlikle ayrı ve olabildiğince özel işler yapan kısımlara ayırın ki program esnek olsun, aynı zamanda kırılganlık ve bağımlılığı düşük olsun.Unutmayın ki 100 küsür satırdan oluşan bir metodun değiştirilmesi veya hata düzeltmesi bir yana, daha küçük parçalara bölünmüş 15 satırlık metodun değiştirilmesi bir yanadır.
    Unutulmaması gereken bir konu SRP 'nin teoride kolay, fakat pratikte uygulamanın hiç de öyle olmadığıdır. Programın nerede küçük kısımlara ayrılması gerektiği kodu yazarken tamamen kestirilememektedir. O yüzden bu işlem ilk başlarda uygulanması çok zor bir yöntemdir.Bir bakışta programı parçalara bölmek tecrübe gerektiren bir iştir, fakat üstte yaptığımız gibi kodlar yazıldıktan sonra tekrar tekrar üstünden geçerek düzeltilip SRP 'ye uygun hale getirilebilir ki biz buna Kod Gözden Geçirme (Refactoring) diyoruz, buradan bu bilgiyi de vermiş olalım :)

Örnek :
  Şimdi ki örneğimizde çok basit bir web sayfası tasarlayıp, input olarak aldığı bilgiyi müşteri bakiyesi ile kontrol ederek yeterli bakiye varsa, db ye kayıt atıp müşteriye de mail gönderen bir tasarım gerçekleştirelim.


















Yukarıdaki örnek işlevsel olarak sadece 1 görev/sorumluluk  üstlenen farklı metodlara bölünmüş olmasına rağmen, birbirinden çok farklı işler yapan bu metodların aynı sınıf içinde yer alması dolayısıyla SRP dizaynına uymamaktadır. Projemizi SRP'ye uygun hale getirmek için Refactoring yaparak, Mail gönderimi ve Müşteri Bakiye Kontrol işlemini ayrı sınıflara alıp tekrar aşağıdaki gibi yazmamız gerekmektedir.


Projemizde farklı işler yapan metodları farklı sınıflara ayırdıktan sonra, hem esneklik hem de kullanılabilirliği sağlamış olduk. Hatta bir adım daha öteye giderek, mail gönderme ve limit sorgulamanın çok genel bir yapı olduğunu göz önüne alarak, projeye isimlendirme standartlarına da uyarak "SRP.Core" diye bir class library ekleyip sınıflarımı, katmanları da birbirinden ayırmış  olduğum "SRP.Core" içerisine ekliyorum ve yeni tasarımım aşağıdaki hale geliyor.

















   Yapıyı bu şekilde oluşturduktan sonra artık farklı projelere referans verilebilir hale getirmiş bulunmaktayım.Şu durumda sadece "SRP.Core" library projesini başka projelere referans vermemiz ortak kullanabilmek adına yeterli olacaktır, aksi durumda koskoca web projesini referans göstermek zorunda kalacaktık ki, bu durumda ne esneklik ne de modülerlikten bahsedebiliriz. Fakat son durumda katmanları ayırarak hem katmanlı ve modüler bir mimari, hem de SRP 'ye uygun dizaynı gerçekleştirip bir taşla iki kuş vurmuş olduk. 
     Böylece SOLID prensiplerinden ilki olan SRP' yi oldukça basit bir şekilde anlatmaya çalıştım, bir sonraki yazım olacak olan "Open/Closed Principle" ile görüşmek üzere.

3 Haziran 2016 Cuma

TASARIM PRENSİPLERİ (Design Principles - SOLID)

       Herkese merhaba, bugün ki ve bundan sonraki muhtemel 4 - 5 yazımda aslında yazmayı iple çektiğim ve gerek yazarken gerekse de uygularken çok zevk aldığım "Design Principles" konusuna değiniyor olacağım. Konuyu daha önce bilen, duyan herkesin "hah şimdi esas konuya geldi" dediğini duyar gibiyim. Bu yazının bugüne kadar ki olan diğer yazılarımdan en büyük farkı, daha çok mimariye/tasarıma giriyor olmasıdır. Tasarım Prensipleri altında anlatacağım konular projelerimizin mimari yapısı ile ilgili olacaktır.
         Tasarım Prensipleri fikrinin ortaya çıkış temeli "Daha İyisini Nasıl Yaparım?" düşüncesidir. Benzer şekilde bu soruyla ilişkili olarak "Daha Hızlı, Daha Kolay Bir Şekilde Nasıl Yaparım?" düşüncesi ve "En İyisini Nasıl Tasarlarım?" sorusunun karşılığı olarak ortaya çıkan kalıplardır. Hayatta her şeyin daha iyisinin zaman içerisinde gelişim sürecine bağlı olarak yapılabileceğini varsayarsak, benzer şekilde yazılan projelerin,programların da daha iyisi olabileceğini sakın unutmayın. İşte tam bu noktada "Nasıl?" sorusunu sorduğumuz her durumda dönüp "Design Principles" konularına bakabilirsiniz, bu sizin için bir anahtar niteliği taşımaktadır.
      Object Oriented Programming (OOP) ile uğraşan herkesin günümüz mimari kuralları çerçevesinde mutlaka bilmesi gereken Tasarım Prensipleri, dünyada hemen herkes tarafından kabul görmüş SOLID kısaltması ile belirtebileceğimiz 5 ana prensipten oluşmaktadır. Bu prensipler sırasıyla aşağıdaki gibidir.

1 - Single Responsibility Principle         (Tekil Somluluk Prensibi)
2 - Open/Closed Principle                      (Açık / Kapalılık Prensibi)
3 - Liskov's Subtitution Principle           (Liskovun Yer Değiştirebilme Prensibi)
4 - Interface Segregation Principle         (Interface Ayırım Prensibi)
5 - Dependency Inversion Principle       (Bağımlılığı Ters Çevirme Prensibi)

       Yukarıda saydığım prensiplerin baş harflerinden oluşan SOLID, sağlıklı mimariler oluşturup sağlam tasarımlar yapmamıza olanak sağlayan prensipler bütünüdür diyebiliriz. Bu prensipleri bilmek ve uygulamak kullandığımız programlama dillerinin (C#, Java,C++ vs) gücünün de farkına varmamızı sağlayacaktır. Şunu belirtmek isterim ki prensipleri ilk başlarda uygulamak hatta anlamak dahi zor olabilir, fakat zaman içinde pratik kazandıkça "Vaay bee böyle bir şey de mi varmış?Ben bugüne kadar ne hamallık yapmışım." diyeceksiniz, buna eminim.
       Bugüne kadar ki tecrübelerimden yola çıkarak şunu gönül rahatlığı ile söyleyebilirim ki, bu prensipleri uygulamak bazen hayat kurtarır. Özellikle çok fazla yazılımcı ile büyük bir firmada büyük projelerde çalışıyorsanız. Mesela 20 küsür yazılımcı arkadaşla aynı proje üzerinde ortak çalıştığım da oldu, tabi burada herkesin aynı bilgi beceri ve kabiliyette olmadığı aşikar. Bu tip durumda herkese belirli standartlarda kod yazdırabilmenin de tek yolu OOP-Design Principles' ten geçer. Biraz daha açmak gerekirse, hem web tabanlı uygulamaların hem android hem de ios cihazların ortak eriştiği yapıların olduğu ayrı ayrı projeler düşünün, birisinde yapılan hatalı bir kod değişikliği domino etkisi yaparak kalan projelerde yıkıma sebep olabiliyor. Bu tip durumlarda yazılan kod ya geri alınmak zorunda kalıyor, ya da sürekli hatalı yapılan işin üstüne hatalı yamalar yapılarak iş iyice içinden çıkılmaz hale gelebiliyor (olmaz demeyin kaç defa başıma geldi, oluyor). Bu sorunlar neticesinde iş dönüp dolaşıp, 5 ana prensibe uymayan hatalı mimari tasarıma dayanıyor.
       Hangi proje üzerinde çalışıyorsanız çalışın, şayet mimari tasarımı sıfırdan kurma gibi bir şansınız varsa bu tip sorunlarla karşılaşmamak adına bu 5 prensipten vazgeçmemek sizi daha sonra doğması muhtemel büyük sorunlardan koruyacaktır. Aksi halde belirli oranda oturmuş mimari yapıyı da oturup sıfırdan yazamayacağınız için, sürekli hatalı ve yeni hatalara gebe yamalar yapmak zorunda kalacaksınız ki iş iyice arap saçına dönecek. Zaman içinde projede çalışan her arkadaşın "Yeter be bıktım bu hatalardan" deyip bir bir yanınızdan istifa edip gittiğine şahit olacaksınız. Yanlış tasarlanmış sistemin içinde boğuşmak sürekli kendini tekrarlayan hataların doğmasına sebep olacaktır. Şurasını unutmayın ki bu hatalar da sizi sürekli mesaiye bırakıp, kendinize ve sevdiklerinize ayırabileceğiniz zamanı çalacak en önemlisi yaptığınız işten zevk almak yerine ızdırap çekmenize sebep olacaktır.
      Anlattıklarımı en başından beri toparlarsanız yapılan mimari hataların domino etkisini kendi hayatınızda dahi hissedeceksiniz. "Abartıyorsun" diyenleriniz vardır belki, ama inanın en ufak abartı olmadan hatta eksiği var fazlası yok diyebileceğim şekilde bizzat yaşadıklarımdan alıntı yaparak yazdım. Sonuç olarak "Sağlam, Esnek, Daha az kodla daha çok iş ve Dinamik" olarak nasıl program yazarım tasasında iseniz şayet, bu prensiplere mutlaka riayet etmenizi öneririm. Evet, bunlara uymadan kod yazılabilir mi? derseniz..Yazılır..Ama kendinizden ve ailenizden çalarak yazılır, üstelik yaptığınızdan işten zevk almadan ve her an yeni hatalar çıkacakmış tedirginliği ile yazılır..
      Bu yazım biraz daha sohbet tadında, birikmiş tecrübelerden bahsederek geçti. Önümüzde ki yazılarda bu prensipleri teker teker irdeleyip, bol örnek yapıyor olacağız. Bu konular zor evet ama bence çok zevkli, sonraki yazılarda görüşmek dileğiyle..

2 Haziran 2016 Perşembe

Global.Asax

      Herkese merhaba, bu yazımda web sitelerinde kullanılan "Global.Asax" dosyası ve niteliklerinden bahsediyor olacağım. Global Application Class olarak tanımlanan Global.Asax dosyası Web.Config gibi özel bir dosyadır ve web sitesinde sadece 1 tane bulunur. Sadece ve sadece "Server" üzerinde çalışır, dışarıdan çağırılamaz. Uygulama seviyesinde olaylar, nesneler ve değişkenleri yönetmek amacında kullanılır. Nasıl ki her web sayfasının kendine ait bir life cycle (Init, PreInit,OnLoad vb) varsa, uygulama düzeyinde Global.asax dosyasının da life cycle' ı vardır.
      Bu dosya üzerinde tanımlanan bazı yordamlarda site ile ilgili genel işler yaptırılabilir.Örnek verecek olursak herhangi bir .aspx sayfasında "Page_Load()" metodunda sayfa yüklenirken olayları nasıl yönetebiliyorsak, uygulama düzeyinde de Global.asax içerisinde ki "Application_Start()" metodunda web sitesi ilk kez başlatılırken ki olayları yönetebiliyoruz. 
     Projemize sağ tıklayarak Add - Global Application Class dersek, projemizin kök dizininde Global.Asax dosyamızı oluşturmuş ve aşağıdaki yordamlar hazır halde bulmuş oluruz.



























Global.asax içerisindeki metodlara bakacak olursak:

Application_Start : Uygulama, Web Server (IIS) üzerinde ilk Start edildiği anda sadece 1 kez çalışan yordamdır.Bireysel kullanıcı bazlı çalışmaz. Uygulama ilk başladığı andaki olayları yönetmek için kullanılır.

Application_End : Uygulama,Web Server (IIS)  üzerinde sonlanırken çalışır.

Session_Start : Bireysel kullanıcı uygulamaya ilk kez eriştiğinde, yani client' a ait session başlarken çalışır.

Session_End : Bireysel kullanıcıya ait session sonlandığında çalışır. InProc modda kullanıcı uygulamadan çıkarsa çalışır.

Application_BeginRequest : Server'e uygulama düzeyinde gelen her Request' te (istekte) çalışır.

Application_EndRequest : Uygulamaya gelen Request tamamlanıp sonlandığında çalışır.

Application_PreRequestContent : İçerik bilgileri tarayıcıya gönderilmeden hemen önce çalışır

Application_PreSendRequestHeaders : Başlık bilgileri tarayıcıya gönderilmeden hemen önce çalışır.

Application_AuthenticateRequest : Kullanıcı kimlik doğrulaması yapılmadan hemen önce çalışır.

Application_AuthorizeRequest : Kullanıcı yetkilendirilmesinden önce çalışır.

Global.asax dosyasının kullanım amaçlarına bir kaç örnek verecek olursak;

- Uygulama başlar başlamaz çalışmaya başlayacak, ve uygulama boyunca çalışmaya devam edecek bir zamanlanmış görev "Application_Start" metodunda tetiklenebilir.
- Uygulama bazında oluşacak hata loglanması ve yönetimi "Application_Error" metodunda yazılabilir
- Uygulamadaki anlık kullanıcı sayısını bulmak için "Session_Start" ve  "Session_End" metodları kullanılabilir.

Örnek - 1 :
  Uygulamadaki anlık kullanıcı sayısını bulmak isteyelim. Hemen bir "Web Site" oluşturup, Global.asax dosyasını ekliyoruz. İçeriği aşağıdaki gibi değiştirip, Application State içine uygulamanın herhangi bir sayfasında kullanılmak üzere kullanıcı sayısını atıyorum.






    
sonra herhangi bir .aspx sayfası oluşturup, içerisinde kullanıcı sayısını Application State üzerinden alıp ekrana basalım.













Sonuç :






Hatta "Session_Start" matodu içinde kullanıcı bazında session timout süresini 1 dk olarak ayarlayıp, süre sonunda "Session_End" metoduna düştüğünü izleyebilirsiniz.








Örnek - 2 :
    Şimdi ise uygulamanın herhangi bir sayfasında alınabilecek herhangi bir hata durumunda, uygulamayı oluşturmuş olduğumuz hata sayfasına yönlendirip tüm hataları aynı formatta görmeyi deneyelim.Bu durumda genel bir hata sayfası yapıp, bu hata sayfasına uygulamanın herhangi bir yerinde oluşacak hatada yönlendirme işlemi için Global.asax dosyasını kullanmamız gerekmektedir.

Hata sayfamızı oluşturup QueryString ile gönderilen parametreyi ekrana basan bir yapı oluşturuyoruz.












Sonra sayfamıza bir textbox bir de buton ekleyip, butonun click eventinde hata yapımızı tanımlıyoruz. Tabi burada sayfaya ait "Page_Error" metodunu çağırıp son oluşan hatayı bir üst katmana fırlatıyoruz.




























En son Global.asax içerisindeki "Application_Error" metodunda uygulamanın herhangi bir noktasında oluşan hatayı genel hata sayfama yönlendirerek, kullanıcıya mesajı gösteriyorum.
















Şimdi hatalı bir giriş yaparak testimi gerçekleştiriyorum ve sonuç :


            Sonuç olarak unutulmamalıdır ki, Global.asax dosyasında yapılan işlerde dikkatli olunmalıdır, çünkü bu yapılan işler sonucunda web sitesini restart etmek ve Application state içerisinde yer alan tüm verileri kaybetmek zorunda kalabiliriz.