SOLID Prensipleri : 03 Liskov Substitution Principle

By Burak Tungut - 15.6.2016 - 5 Yorum - Kategori Design / Architectural Patterns

Selamlar herkese,

Son 1 haftadır bazı çalışma arkadaşlarımın "Yazmaya neden ara verdin?" tepkilerine daha fazla dayanamıyor ve bu yazımızda SOLID prensiplerinden Liskov Substitution - Liskov'un Yerine Geçme prensibini inceliyor olacağız. Yazmam için sürekli beni dürten sevgili çalışma arkadaşım Barış BIYIK'a teşekkürlerimi buradan da iletmek isterim :)

Liskov Substitution aslında Prof. Barbara Liskov ismindeki bir bilim insanının tasarladığı bir prensip.
Prensip bize der ki ;

"Aynı base sınıftan üretilen tüm alt sınıflar, birbirlerinin yerine kullanılabilir olmalıdır. Alt sınıflara özel bir istisnai durum kesinlikle oluşmamalıdır."

Bazen abstract class'lar ile interface'ler arasındaki farkın ne olduğuna dair sorularda alıyor olabilirsiniz. Aslınd bu prensibin amacı bu iki tip abstraction yapmamızı sağlayan tip arasındaki en büyük farkı göstermekte. Birazdan yapacağımız örnek ile bunu çok daha iyi anlıyor olacağız.

 

Liskov Substitution Principle

 

Küçük Bir Senaryo İle Liskov Substitution Principle

Farz edelimki ürünlerin ve ürünler üzerinde çeşitli business'ları işletebilecek üyelik tiplerinin olduğu bir proje geliştiriyoruz. Çok minimal bir ürün tipi tasarlayalım. Ben aşağıdaki gibi bir şeyler tasarladım;

public class Product
{
    public string Name { get; set; }
    public decimal Price { get; set; }
    public List<string> Features { get; set; }
}

Devamı

Gerçek Hayattan Factory Method Design Pattern Örneği

By Burak Tungut - 5.6.2016 - Kategori Design / Architectural Patterns

Selamlar herkese,

Bu yazımda sizlerle gerçek bir uygulama ile Factory Method Design Pattern'in nasıl uygulanacağını paylaşacağım. Geçtiğimiz aylarda üzerinde çalıştığımız ana modüllerimizden birinde sürekli benzer istek ve değişikliklerin geldiğini fark ettik. Uygulamada ki genel gidişimizde sürekli if-clause'lar üzerine dayanmaya başlayınca buna bir dur demek istedik ve biraz sonra sizlerle paylaşacağım bir geliştirme gerçekleştirdik. Dilerseniz uygulamaya geçmeden önce ihtiyacın ortaya çıkmasına sebep olan istekleri biraz inceleyelim.

Not : Makale aşağıda listelediğim bazı konuları içerir. Aşağıdaki konular hakkında bilgi sahibi iseniz harika! Değilseniz makalenin daha anlaşılabilir olabilmesi için bir kaç saatinizi ayırarak önce bu listeyi halletmenizi önemle rica ediyorum :)

  • Thread Safety
  • Stateless-Stateful
  • Principal & Identity
  • Reflection

"Backend'i Aynı Olsun, Frontend'i Farklı"

Geliştirmekte olduğumuz bir ürünümüz başlangıçta bir kullanıcı tipine sahip iken iş birimlerimizden bu tiplerin çoğalacağı haberlerini aldık. Fakat her kullanıcı aynı veriye erişirken, verilerin gösterim biçimleri farklı olacaktı. Benzetmek gerekirse uygulamada kullanıcı tipi farketmeksızın herkes beş adet ürün görecek lakin kimi kullanıcı tipleri bunları slider ile görecek iken kimileri bir table içerisinde görecekti.

İsteğe çevik bir şekilde cevap vermek adına geliştirmeleri tamamladık. Lakin MVC projemizde fark ettik ki Controller ve Action'larımız hiç değişmez iken bunları execute ettiği View'larda sürekli if blokları yazmak durumunda kaldık. Tıpkı aşağıdaki gibi;

<h2>Product</h2>

@if (userType == FactoryMethod.Core.UserType.New)
{
    <b>Slider ile datayı görüntüle</b>
}
else if (userType == FactoryMethod.Core.UserType.Old)
{
    <b>Table ile datayı görüntüle</b>
}

Bu if-clause'ları View'dan alıp Action'lara koyarsakta çok bir değişiklik olmayacaktı. Buyrun bakalım;

public class ProductController : Controller
{
    UserType userType = UserType.Old;

    public ActionResult Index()
    {
        //Business call

        if (userType == UserType.New)
        {
            return View("IndexOfNewUsers.cshtml");
        }
        else
        {
            return View("IndexOfOldUsers.cshtml");
        }
    }
}

Bu iki yapının arasında kalsak ben kesinlikle ikinci olanı seçerdim. Sonuçta tüm business ve backend aynı iken sadece frontend değişiyor ise View'ları ayıralım. Hangi View'ın execute edileceğine ise bir önceki pipeline katmanında karar verelim.

Başka Kullanıcı Tipleri Eklenmeye Devam Ederse?

Son yaptığımız haliyle uygulama daha kabul edilebilir bir hal alabilir. Fakat bu yapıyı inşaa etmemize neden olan istekler gelmeye devam ederde yeni kullanıcı tipleri eklenmek isterse sürekli her Action'a if blokları mı yazacağız? İşte bu noktada artık bir dur dememiz gerekir.

Bizim artık öyle bir yapıya ihtiyacımız var ki ne View ne de Action bir state'e bağlı olsun. Yani stateless olsun. Action'ımız Business Engine'ler ile konuşsun, gerekli model'i intialize etsin ve View'a versin. View'da asla business içermesin gerekli veriyi istendiği gibi göstersin.

O zaman bu stateless olarak belirlediğimiz scope'un dışında kullanıcı tipine (ya da herhangi stateful bir olgu) göre akışı değiştirecek, ilgili View'ı bulup execute edecek bir yapıya ihtiyacımız var. 

Biraz detaylıda olsa problemimizi tanıdığımıza göre gerçek bir uygulama ile sorunumuzu çözmeye başlayabiliriz.

Devamı

SOLID Prensipleri : 02 Open Closed Principle

By Burak Tungut - 17.4.2016 - Kategori Design / Architectural Patterns

Open Closed PrincipleHerkese selamlar,

Havaların ısındığı şu güzel günlerde gezip tozmanın yanısıra birazda sizlerle paylaşım yapmak istedim. Ancak işin kötü tarafı bir kaç saate sahile inip orada kitap okumak gibi bir planım var, umarım bu nedenle makaleyi aceleye getirmem. Getirirsemde şimdiden affola :)

Geçtiğimiz aylarda SOLID prensiplerini anlatacağım bir makale dizisine başlamıştık. Bu makale ile 2. prensibimiz olan Open Closed Principle'a değineceğiz.
Prensibin bize anlatmak istediği şeyi şu şekilde ifade edebiliriz; "modüller gelişime açık, değişime kapalı olmalıdır".

Hiç şüphesiz modüler yapıların inşaa edildiği mimarilerde en çok kullanılan prensiplerden biri Open-Closed olup, yanına DI framework'ler ya da abstract factory implementasyonları kullanılır. Şimdi bu teorik yaklaşımları biraz uygulanabilir, el ile tutulabilir hale getirelim.

Problemi Tanıyalım

Çok ilginç örnekler ile implementasyonlar yapmaya bayılırım, bilirsiniz :) Bu prensibi anlatırken özellikle ilkokul ve lisede çokça rastladığımız faiz problemlerinden esinlendim. Hatta içerdiği bilinmeyenlerin (a, n, t) birleşimini alarak Ant Formül'ü diyede bir isme sahiptir. Sizlerde benim gibi yıllardır bu formülü kullanmamış olabilirsiniz. Onun için öncelikle faiz problemlerini çözmemize yardımcı olan Ant Formül'ünü bir hatırlayalım.

F = (a * n * t) / x

Bu formülde F faiz miktarını, a anaparayı, n faiz yüzdesini ve t zaman birimini gösterirken x ise yıllık, aylık, haftalık ya da günlük periyotlar olmak üzere sırasıyla 100, 1200, 5200 ve 36000 değerini alan bir bilinmeyendir.

Open-Closed Prensipsiz Uygulanışı

Prensip ya da prensipsiz eminim yüz farklı şekilde bu problemi çözecek bir algoritma inşaa edilebilir. Ben aklıma gelen ilk yöntemi seçiyorum ve öncelikle aşağıdaki gibi bir enumeration yaratıyorum.

public enum Interval
{
    Yearly,
    Montly
}

Arından InterestCalculator adında bir class ve içerisinde Calculate adında bir method yaratıyorum. Methodumuz parametre olarak a, n, t bilinmeyenlerini ve bir Interval alıyor, geriye ise decimal tipinde bir değer döndürüyor olsun. Tıpkı aşağıdaki gibi;

public class InterestCalculator
{
    public decimal Calculate(int a, int n, int t, Interval interval)
    {
        decimal retVal = 0;

        if (interval == Interval.Yearly)
        {
            return (a * n * t) / 100;
        }
        else if (interval == Interval.Montly)
        {
            return (a * n * t) / 1200;
        }

        return retVal;
    }
}

Devamı

SyncedOutputCache ile Aynı Anda Invalide Olan Output Cache Implementasyonları

By Burak Tungut - 2.3.2016 - Kategori Asp.Net MVC

Selam millet!

Küçük bir ev taşıma, yerleşme arasından sonra tekrar beraberiz. Aslında Elasticsearch ve SOLID konularında ayrı ayrı yazı dizileri başlatmıştım fakat okuyacağınız bu makalede bunlara hiç değinmedim. Geçtiğimiz haftalarda iş yerinde geliştirdiğimiz bir üründe doğan bir ihtiyacımıza karşı bir implementasyon yapmak durumunda kaldık. Size bu tecrübemi aktarmak istiyorum.

OutputCache Şart Oldu

Öyle bir ürün düşünelimki kullanıcı adı ve şifremiz ile authenticate oluyor ve uygulamada gezinmeye başlıyoruz. Fakat öyle sayfalarımız varki içerisindeki sayıların, data'ların uyuşması, tutarlı olması gerekli. Diğer bir yandan ise performans için disk'ler üzerindeki IO'yu düşürmek istiyoruz.

Böyle bir durumda akla Output Cache kullanmak gelebilir. Fakat Asp.Net MVC içerisinde halihazırda var olan OutputCacheAttribute bu ihtiyacımızı tam olarak karşılamayacaktır. Çünkü var olan output cache implementasyonu söz konusu action execute edildikten sonra çalışır. Bu da sayfaların (ya da partial'ların) cache'e alınmış hallerinin aynı anda invalide olmayacağı ihtimali anlamına gelir.

Anlaşılabilir olması için şöyle bir örnek ile ilerleyelim. A sayfamızda sipariş verilmiş ürünlerin sayısı gösterilirken, detayları içeren sayfası sipariş verilmiş olan ürünlerin detayını göstersin. Eğer A ve B sayfaları aynı source'u kullanmıyorsa bu sayfaların output cache'e alınması tutarlı sayıların gösterilmesi yönünde bizi zor duruma sokabilir. Birinin cache'i 3 dakika diğerinin 15 dakika sonra invalide olacağını düşünürsek yeni gelmiş olan kayıtlara ait data ya da sayılar birinde gözükürken diğerinde gözükmeyecektir.

İşte bunun gibi durumları ortadan kaldırabilmek için tüm cache'leri sync hale getirmemiz gerekebilir. Biz bunu yaparken var olan OutputCacheAttribute'ünü inherited etmiş yeni bir sınıf kullanacağız. Amacımız ise x zamanında gelen ve output cache'e alınmak istenen bir request'in x+10 ya da x+60 gibi bir sürede invalida olması değil, bir sonraki 10. ya da 60. (bu sayı bize kalmış, parametrikte olabilir) dakikada invalide olması.

Şöyle bir senaryo düşünebiliriz. A sayfasına 23.03'de girildi ve cache'e alındı. Bu cache'in invalide işlemi 23.10'da yapılmalı. Hal böyleyken 23.07 de dahi B sayfasına girilse burası için yaratılan cache'de 23.10'da invalide olacaktır.

Yani cache'lerimiz sonraki dakika bloğunu tamamlayacak şekilde expire olmalı.

Expire Zamanının Hesaplanması

Şimdi yukarıdaki anlatılan business'a göre expire zamanının hesaplanmasını gerçekleştirelim. Öyle bir method yazmalıyız ki parametre olarak expire edilecek dakikayı almalı (10,15,30 gibi) ve geriye söz konusu cache'in tam olarak hangi zamanda invalide edileceğini bildiren bir değer döndürmeli. Bu değerin DateTime olması daha anlaşılabilir olacağı için aşağıdaki gibi bir method yazabiliriz.

private DateTime GetExpireDateTime(int expirationMinute)
{
    var retVal = DateTime.Now;

    retVal = retVal
        .AddSeconds(retVal.Second * -1)
        .AddMilliseconds(retVal.Millisecond * -1);

    if (expirationMinute == 60)
    {
        retVal = retVal
            .AddHours(1)
            .AddMinutes(retVal.Minute * -1);
    }
    else
    {
        retVal = retVal
            .AddMinutes(expirationMinute - retVal.Minute);
    }

    return retVal;
}

Bu methoda gelecek olan expirationMinute aslında bir expire block niteliği taşıdığı için 60 gelme durumu da söz konusu olacaktır. Örneğin 15. dakikanın katlarında invalide olması istenen sayfalarımız varsa herhangi bir saatin 45. dakikasından sonra gelecek olan bir request sonraki saatin ilk saniyelerinde invalide olmalıdır. Bu nedenle 60 olması durumunda bir sonraki saatin dakika, saniye ve saliselerini siliyoruz.

Devamı

SOLID Prensipleri : 01 Single Responsibility ve Agile Metodolojilerde Önemi

By Burak Tungut - 15.12.2015 - Kategori Design / Architectural Patterns

Herkese selam laugh

Paylaştığım iki adet elasticsearch makalesinden sonra araya başka bir makale koymak iyi olur diye düşündüm. Hep aynı konuları işleyen türk dizileri gibi on tane elasticsearch makalesi eminim düzenli okurlarımında canını sıkardı. Hem ilgilenen var, ilgilenmeyen var efendim! Öyle değil mi?

Gelelim okumaya başladığınız makalemizin konusuna. Gariptir ki bu makalede aslında SOLID prensiplerini işleyeceğim makale serisinin ilk adımı niteliği taşıyor. Yani paralelde başka bir seriye daha girmiş olduk. Sonumuz hayır olsun.

SOLID prensipleri 2000'li yıllarda Robert C.Martin tarafından ortaya atılmış beş adet prensipten oluşur. SOLID ismi ise bu beş presibin isimlerinin ilk harflerini alarak oluşturulmuştur. Yabancı kaynaklarda çevrilmiş haliyle SOLID presinsiplerinin amacı ise kısa vadede bazen taklalar atarak uzun vadede daha maintain (bakım) edilebilir ve extensible (genişletilebilir, esnetilebilir) mimariler kurulmasıdır. 

SOLID ve Agile Metodolojilerdeki Önemi

Günümüzde SOLID'in popülerliğinin arttığını şahsen görüyorum. Bunun nedenlerinden biri ise aslında Türkiye'de agile metodolojilerin git gide kullanılmaya başlanması. Sonuçta ne kadar agile software team o kadar refactoring, maintain edilebilirlik ve haliyle SOLID implementasyonları diyorum.

Bazen öyle legacy code içeren projeler görüyoruz ki bir sınıfında binlerce satır olabiliyor. (5000 satırı görmüşlüğüm var surprise)  Bunun en büyük nedenlerinen biride o yıllarca üstüne business eklenerek büyümüş olan projelerin her bir modülünün tamamiyle bir kaç developer sorumluluğuna bırakılması (A modülüne iş var. Kim bilir? Hasan bilir!)
Bir de ürün yetiştirmek için gerekli zamandan daha hızlı geri dönüşler bekleyen bir firmadan bahsediyorsak o modüllerden okunabilir kod çıkma olasılığı gittikçe düşüyor. Velhasıl sayabileceğimiz bir çok madde ve dış etkenler mevcut.

Konuyu çok dağıtmayayım. Bunlar benim şahsi görüşlerim idi. Yukarıda verdiğim örnek ile geçmişte uygulanan metodolojilere dil uzatmak istemedim. Sonuçta elde olan bu ise ve koşullar onu gerektiriyorsa pekte yapılabilecek birşey kalmıyor smiley

Şimdi SOLID prensiplerinden Single Responsibility yani Tek Sorumluluk prensibini incelemeye başlayalım.

Solid prensipleri

Devamı
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
Facebook
Son Yorumlar