Herkese merhaba. Bu yazıda en temel yazılım geliştirme ilkesi olan SOLID’den bahsedeceğim. S.O.L.I.D aslında 5 temel ilkenin bir araya getirilerek kısaltılmış halidir. İlk olarak 2000 yılında Robert C. Martin tarafından “Design Principles and Design Pattern” makalesinde ortaya atılmıştır. Prensiplerin isim kısaltmasını ise Michael Feathers yapmıştır. Martin makalesinde başarılı bir yazılımın değişmesi ve gelişmesi gerektiğine değinmektedir. Ancak, yazılım değişip geliştikçe daha karmaşık bir hale gelir. Bu nedenle iyi tasarım ilkelerine sahip olmadan geliştirilen bir yazılım katı, kırılgan, hareketsiz ve zararlı bir hale dönüşür. Bu ilkeleri de sorunlu tasarım modelleriyle başa çıkmak için tasarlamıştır. SOLID yazılım ilkelerini kullanarak daha esnek ve geliştirmeye açık uygulamalar yazabiliriz. Bu yazıda kullandığım örnek kodlara buradaki github repomdan ulaşabilirsiniz.
SOLID Nedir?
Yazılımın esnek, yeniden kullanılabilir, sürdürülebilir ve anlaşılır olmasını sağlayan ve kod tekrarını önleyen prensipler bütününe SOLID ilkeleri diyoruz. Bu prensiplerin en geniş hedefi bağımlılıkları azaltmaktır. Bu sayede yazılım geliştirilirken değişen bir kod parçası diğer geliştiricileri etkilemez. Buna ek olarak, tasarımların anlaşılması, bakım yapılması ve genişletilmesi kolay bir hale gelir. Bu da geliştiricilerinin etkili ve agile yazılımlar geliştirmesinin önünü açar.
Yazının girişinde de belirttiğim gibi SOLID prensipi 5 temel tasarım ilkesinin kısaltılmasıdır. Bu ilkeler;
Yukarıdaki her ilkeyi kendi örnekleriyle beraber detaylı olarak inceleyeceğiz. SOLID’e ek olarak KISS, DRY, Reuse Release Equivalence ve Common Closure gibi prensipler de nesne yönelimli programlamada kullanılmaktadır.
1. Single Responsibility Principle
Single Responsibility ilkesi, her metodun ya da sınıfın sadece tek bir sorumluluğu olması gerektiğini ifade eder. Yani, bir sınıf ya da metodun içerisine birden fazla iş yüklemek yerine, işleri parçalara ayırmak daha uygundur. Gereksinimler değiştiğinde, birden fazla sorumluluğu olan bir yapıda değişiklik yapmak daha zor ve zaman alıcı olabilir. Ancak sorumluluklar ayrıldığında, sadece ilgili parçayı değiştirerek kodu daha kolay yönetebiliriz. Bir sınıfın sorumluluğu arttıkça, değişimlere de o kadar maruz kalır.
Peki bu prensip bize nasıl daha iyi bir yazılım mimarisi oluşturmakta yardımcı olabilir? İşte birkaç önemli faydası:
- Test: Sadece bir sorumluluğu olan bir işlevi test etmek, daha az test senaryosu gerektirir.
- Lower Coupling (Düşük Bağımlılık): Az sorumluluk, daha az bağımlılık anlamına gelir. Bu da kodun daha esnek ve yönetilebilir olmasını sağlar.
- Organizasyon: Küçük ve iyi organize edilmiş sınıflar içerisinde arama yapmak, monolitik yapılara göre çok daha kolaydır.
Örnek olarak, kullanıcı giriş modülü tasarlayalım. Kullanıcının bütün bilgilerini tek bir sınıf içerisinde toparlayalım.
class User { public string Name { get; set; } public string Lastname { get; set; } public string Email { get; set; } public string Password { get; set; } public void Register() { Console.WriteLine("Your registration is completed."); } public void Login(string mail, string password) { Console.WriteLine($"You are logged in with {mail}"); } public void Logout() { Console.WriteLine("You are logged out."); } public void SendMail() { Console.WriteLine("Mail has been sent to your mail."); } public void SendSms() { Console.WriteLine("SMS has been sent to your phone."); } public void ChangePassword() { Console.WriteLine("Your password has been changed."); } }
Bu sınıf içerisine yazdığımız tüm metotlar düzgün çalışsa bile, geliştirme devam ettiği sürece okunabilirliği azaltır. Bu servisi Single Responsibility Principle ilkesine göre her bir sınıfı kendi sorumluluklarına göre düzenlememiz gerekir.
class User { public string Name { get; set; } public string Lastname { get; set; } public string Email { get; set; } public string Password { get; set; } }
Şimdi düzenleyeceğimiz kısmı Interface Segregation Principle kısmında daha detaylı açıklayacağım. Şimdilik şöyle düşünebiliriz; SendMail ve SendSMS metotları aslında aynı işi iki farklı kanal aracılığıyla yapıyor. Bu nedenle Send işlemini soyutlaştırmamız gerekiyor. Bunu da interface‘leri kullanarak sağlıyoruz.
interface ISendService { void Send(); }
Aynı şekilde user işlemlerini de soyutlaştırıyoruz.
interface IUserService { void Register(); void Login(string mail, string password); void Logout(); }
Daha sonrasında sırasıyla bu interface’lerden türeteceğimiz servisleri yazıyoruz.
class MailService : ISendService { public void Send() { Console.WriteLine("Mail has been sent to your mail."); } } class SMSService : ISendService { public void Send() { Console.WriteLine("SMS has been sent to your phone."); } } class UserService : IUserService { public void Login(string mail, string password) { Console.WriteLine($"You are logged in with {mail}"); } public void Logout() { Console.WriteLine("You are logged out."); } public void Register() { Console.WriteLine("Your registration is completed."); } }
Burada interface içerisinde yazılan metotların içerisinde kontrollerimizi yazabiliriz. Bu sayede okunabilirliği yüksek ve modüler bir sistem kurmuş oluruz. Daha sonradan ekibe katılan birisi bu servislerin üzerinde kolaylıkla düzenleme yapabilir.
Bu yazıyı daha fazla uzatmak istemediğim ve her bir ilkeyi detaylı bir şekilde anlatmak için her birini ayrı bir yazıda ele alacağım. Sonraki yazımda Open/Closed Principle üzerinde inceleme yapacağız. Bir sonraki yazıya kadar kendinize çok iyi bakın! 😊
Bir yanıt yazın