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

Single Responsibility Principle - Tek Sorumluluk Prensibi

SRP'nin temel amacı basit ve özdür. Bizlerden tek istediği bir sınf sadece bir işten sorumlu olmalıdır. Şayet bu prensibe uygun geliştirmeler yapmıyorsak esnek olmayan ve binbir tane işi yapan bir sınıfın refactor edilmesi inanılmaz efor isteyebileceği gibi yapılan değişiklikler 10 katlı bir binanın ortasından bir katı çıkarmaya çalışmak kadar tehlikelide olabilir.

Çok basit bir örnek ile dahi bunu açıklayabiliriz. Elimizde özgeçmişlerin olduğunu ve özgeçmişler üzerinde pozisyona atama, pdf olarak export etme ya da yazdırma gibi uygulanması gereken business'larımızın olduğunu düşünelim.

Aşağıdaki gibi bir sınıf bizim işimizi görecektir;

	public class Resume
    {
        public int Id { get; set; }

        public string Fullname { get; set; }

        public DateTime BirthDate { get; set; }

        public void AssignNewPosition(int positionId)
        {
            // Some actions
        }

        public Stream ExportAsPdf()
        {
            // Some action
            return new MemoryStream();
        }

        public string RenderHtmlForPrinting()
        {
            // Some actions
            return string.Empty;
        }
    }

Bir bakalım neler yapmışız. Elimizde Resume adlı bir sınıf var ve içerisinde 3 adet business method içeriyor. Ops! Hadi code convention'a uygun geliştirme yapmadığımızı bile düşünsek bu sınıf hem Resume'nin bir modelini içeriyor. Belki bir database tablosunun yansıması belki de bir DTO. Ama hemen altında business method'lar mevcut.

Zaten böyle bir başlangıç ile kafadan 1-0 yenik başladık. O zaman bu kodu şöyle refactor edelim;

    public class Resume
    {
        public int Id { get; set; }

        public string Fullname { get; set; }

        public DateTime BirthDate { get; set; }
    }

    public class ResumeManager
    {
        public void AssignNewPosition(Resume resume, int positionId)
        {
            // Some actions
        }

        public Stream ExportAsPdf(Resume resume)
        {
            // Some action
            return new MemoryStream();
        }

        public string RenderHtmlForPrinting(Resume resume)
        {
            // Some actions
            return string.Empty;
        }
    }

İlk değinebileceğimiz sorunu en azından ortadan kaldırdık. Model niteliği taşıyan Resume sınıfımızda hiç bir business method kalmadı. Bu artık sadece bir model. İstersek EF'için entity niteliği taşıyabilir. İstersek bir Web API için Response.

Şimdi business methodları taşıdığımız ResumeManager adlı sınıfa bir bakalım. Bu sınıfta sadece methodlar var. Başta güzel gözükebilir. Fakat birbirinden bağımsız methodları içererek geliştirilmeye kötü başladığı için git gide Resume ile ilgili her işi taşıyacak bir sınıf haline gelebilir.

Şöyle düşünelim Pdf olarak export edilebilecek başka bir nesnemiz daha bu mimariye eklenmek istenebilir. O zaman Pdf export ile ilgili işlemleri (Resume'ye spesifik business'lar içermediği sürece) bu sınıf içerisinde tutmak mı daha mantıklı olur? Yoksa sadece pdf exporting ile sorumlu olacak başka bir sınıfta mı?

Parantez içerisinde belirttiğim gibi eğer pdf oluşturmada nesneye spesifik bir business yer almıyorsa kesinlikle bu işin bambaşka bir yerde yapılması gerekli. Aksi söz konusu olsaydı business içeren kod blokları ResumeManager'da geriye kalan pdf exporting ile ilgili kısımları ise başka ve bu iş ile sorumlu olacak sınıfta yapılmalıydı.

O zaman bir refactoring daha yapalım;

	public class Resume
    {
        public int Id { get; set; }

        public string Fullname { get; set; }

        public DateTime BirthDate { get; set; }
    }

    public class ResumeManager
    {
        public void AssignNewPosition(Resume resume, int positionId)
        {
            // Some actions
        }

        public Stream ExportAsPdf(Resume resume)
        {
            return PdfExportingUtil.Export(RenderHtmlForPrinting(resume));
        }

        public string RenderHtmlForPrinting(Resume resume)
        {
            // Some actions
            return string.Empty;
        }
    }

    public static class PdfExportingUtil
    {
        public static Stream Export(string html)
        {
            return new MemoryStream();
        }
    }

Bakınız PdfExportingUtil adında bir static class tanımladık ve içerisinde Export adlı bir method yazdık. Bu sınıf ve methodun tek amacı pdf export etmek. ResumeManager'da ki ExportAsPdf methodu ise business ile ilgili kısımları gerçekleştirip pdf exporting ile ilgili yapılacak işlemleri bununla sorumlu olan sınıf üzerinden gerçekleştiriyor.

Bune benzer bir durum print içinde olsaydı benzer bir refactoring'i onun içinde yapmalıydık. Buna hızlı bir örnek dahi verebiliriz.
Bir Resume'nin html data'sının çok uzun olduğunu düşünelim. Bu durumda uygun bir şekilde paging yapılması gerekebilirdi. Eğer biz bu paging işlemini yine ResumeManager'da yapıyorsak Single Responsibility prensibini yıkmış olacaktık.

İzlememiz gereken sıra şöyle olmalıydı;

  • Resume'ye spesifik business'ları gerçekleştir.
  • Html'i render et.
  • Paging için sorumlu olan sınıfa html'i ilet, sonucu geri döndür.

 

Umarım açıklayıcı bir makale ve örnek olmuştur. Çok yakında diğer SOLID prensipleri ile makalelerime devam edeceğim. Takipte kalın smiley

Yorum Bırak

Facebook
Son Yorumlar