SOLID 1: Single Responsibility Prensibi ya da herkesin ne iş yaptığına kimse karışamaz

Alper Güven
5 min readFeb 6, 2022

--

Devamı gelsin diye kimsenin iple çekmediği, soluksuz hikayelerin yer almadığı, sıkıcı Python yazılarımıza devam ediyoruz. Saatlerini dikdörtgen bir cisme parmaklarını vurarak ışık saçan bir kutu karşısında geçiren insanların, gariban bir makineyle iletişim kurup bazı işleri kendileri yerine yapmasını söyledikleri garip mesleğimizle ilgili yeni bir yazı daha geliyor.

Source: Reddit

Şu an kullandığım Türkçe dili ile buraya geldi bu yazı. Yine o gariban makine sayesinde geldi. Ama sizinle konuşur gibi de konuşmadık bu makineyle tabii. Her dilin dilbilgisi, kuralları, hatta felsefesi olduğu gibi Python dilimizin de var. Ama öncelikle başka bir kısım felsefemizi oluşturuyor; şimdi ben bu bilgisayarla konuşuyorum ama başka bir insan benim bu bilgisayarla ne konuştuğumu anlaması kısmına geldik. Aslında, SOLID prensipleri sadece bilgisayarla olan muhabbetimizi ilgilendirmiyor. SOLID prensiplerinin en büyük amaçlarından biri, bu muhabbete sonradan dahil olanların da kolayca anlamasını sağlamak. Tabii sürdürülebilir yazılım, projenin bozulmadan ilerleyebilmesi amaçlarını atlamıyorum. Fakat önce iletişim. Buraya yazdığım yazıyı belli kurallar çerçevesinde yazmasaydım anlaşamazdık. Noktayı cümlenin ortasına rastgele sallamışım mesela. Noktanın cümlenin sonuna gelmesi gibi bu kurallar, zaman içinde okumayı, düzeltmeyi daha elverişli hale getirmeseydi bırakın bu yazıyı anlamayı birbirimizle de anlaşamazdık (Sanki çok da iyi anlaşamıyoruz gibi insanlık olarak ama neyse). Ha tabi önemli noktalardan biri olarak; kendimi tekrar etmeden yalın bir şekilde yazmam lazım ki okunaklı, derdini direkt olarak basitçe anlatan bir yazı çıksın ortaya değil mi? Herkes daha rahat anlasın. İşte SOLID hakkında yukarıda gevelediklerim, aşağıdaki üç ilke için var gibi geliyor bana. Şöyle ki; i) ben yazılımcı olarak bilgisayarla konuşurken hata yaptıysam, sonradan gelen zihninde düzeltir, okumaya devam eder, ii) yalın olmalı ki okurken saçma, alakasız bulmasın, iii) yazı kendini tekrar etmesin ki ne iş yaptığı anlaşılsın ve zamanımızı çalmasın. Başka bir insan benim bu bilgisayar ile ne konuştuğumu bu üç ilke çerçevesinde çok daha rahat anlayabilir. Yine bu üç ilke gerçekleşirse, temelinden bozulmaması için maksimum gayret gösterilen bir yazılım ortaya çıkar.

Source: Murat Palta

O zaman şimdi bu yazımızda gelelim SOLID’in S’sine. Takdim edeyim kendisini: Single Responsibility Principle. Buyursunlar efendim. İsminiz çok artistikmiş. Ama Türkçe ifade etmek zorundayım, artistiğiniz bize sökmez sayın Tek Sorumluluk Prensibi. Böyle de fena değil aslında. Kendisi SOLID’in ilk prensibi. Şimdi düşünelim; oyun oynuyorsunuz veya yemek yiyorsunuz. Bunları yaparken elleriniz, parmaklarınız, gözleriniz gibi uzuvlarınıza ihtiyacınız var. Oyun oynarken birisi aradı ve telefonla konuşmaya başladınız. Koordinasyon bozuldu, konsantrasyon azaldı, muhtemelen oyunda sıkıntılar başlar. Yemek yerken muhabbet ettiğinizde yemeğin lezzeti her lokmanızda aynı tesiri yapmaz, hatta yemeği dökme ihtimaliniz artar, bir pilav tanesinin ağzınızdan kontrolsüz şekilde meçhule doğru yol alması da söz konusu. Noldu peki? Single Responsibility Principle kardeşimizi ihlal ettiniz. Onu küçümsediniz, yakışmadı… Peki başka bir değişken ya da ilgilenmeniz gereken başka bir iş olmasa sadece oyun oynasaydınız, kesinlikle daha başarılı olacaktınız. Sadece yemek yeseydiniz daha leziz bir zaman dilimi ortaya çıkacaktı. Mesela şu video kadar iyi Tek Sorumluluk Prensibi anlatan bir kesit görmedim. Gerekirse telefonunuz denize düşecek ama sorumlu olduğunuz yemeği yiyeceksiniz. Anlatmaya çalıştığım aslında buydu. Bir method bir iş yapacak kardeşim! Bir sınıf bir görev için yazılacak!

Şimdi aşağıdaki misal alalım:

# Aşağıda iki işi birden yapan bir class var
class Musteri:
dukkan_ismi = "f_society_coffee_shop"
musteri_ismi = ""
def __init__(self, musteri: str):
self.musteri_ismi = musteri

def get_musteri_ismi(self):
print('Musteri geldi')
def save(self, musteri: Musteri):
pass

Yukarıda gördüğünüz gibi, hem müşteri özelliklerinden hem de müşteri veritabanı yönetiminden sorumlu bir Musteri sınıfımız var. Uygulama, veritabanı yönetim işlevlerini etkileyecek şekilde değişirse, yeni değişiklikleri bu sınıfa eklemek için Musteri sınıfı özelliklerini kullanan sınıfların hepsine müdahele etmek zorunda kalırız. Yukarıda sadece ismiyle ilgilendik ama bir çok müşteri bilgisini kaydettiğimizi düşünün, bu sınıfta yaptığımız değişiklikler yetmeyecektir. Yani dediğimiz gibi bütün olarak Musteri sınıfı kullanan her yere bulaşacağız. Halbuki şöyle olsaydı:

class Musteri:
dukkan_ismi = "f_society_coffee_shop"
musteri_ismi = ""
def __init__(self, musteri: str):
self.musteri_ismi = musteri

def get_musteri_ismi(self):
print("Musteri geldi")
class MusteriDataBase:
veritabani_ismi = "musteri_db"
def get_musteri(self, id) -> Musteri:
print("Musteri id")
print(id)
def save(self, musteri: Musteri):
print("Musteri DB kayit")

Yani sınıfları böldük. Veritabanı kayıt sorumluluğunu başka bir sınıfa verdik. Bu sınıf artık diğer sınıftan habersiz olarak işini yapıyor. Bu veritabanı sınıfında değişikliğe mi ihtiyacınız var? Önden buyrun ve sadece veritabanı işlemlerini içeren sınıfı değiştirin. Çiçek gibi.

Birde metotlar özelinde görelim:

def decide_response(price: Decimal):
threshold = 25
i = 0
if price * 0.95 < threshold:
logger( log, 'Price is: {0}' )
f = open("demofile.txt", "r")
lowPrice = f.read(5))
while price < lowPrice:
print(i)
lowPrice += 0.95
return i
...

Mesela yukarıdaki metot içerisinde bir hesaplama var. Fakat hesaplamanın arasında durduk yere dosyadan okuma yapılmış. Yav siz nereden çıktınız? Bu metodu bu şekilde yazdığımız için başka yerde dosyadan okuma yapmaya ihtiyaç duyarsak tekrar aynı şeyi yazacağız. Yani kendimizi tekrar edeceğiz. Bu dosyadan okumayı, sadece dosyadan okuyan ve bir iş yapan bir metot içerisine yazsak ve herkes isteyince kullansa mesela:

def decide_response(price: Decimal):
threshold = 25
i = 0
if price * 0.95 < threshold:
logger( log, ‘Price is: {0}’ )
lowPrice = read_from_file(5)
while price < lowPrice:
print(i)
lowPrice += 0.95
return i
def read_from_file(fileName: String, howManyChar: int):
f = open(fileName, “r”)
return f.read(howManyChar)

Gördüğünüz gibi ne kadar ateşli bir metot oldu, herkes peşine düşecek bu dosyadan okuma metodunun. Başka bir yerde bu metoda ihtiyaç mı oldu? Gel kardeşim bu dükkanın garantisi biziz yav. İstediğin dosyadan istediğin kadar karakter oku. Mesela “logger” metodu da aynı şekilde kullanılmış.

Method özelinde mesela; bir ürünün miktarı azaldı, güncellenmesi gerek ve sonra tüm sorumlularına bildirim atılacak. Bunları tutup bir methoda koymuyoruz. Miktarı güncelleyen method ayrı, bildirim atan ayrı. Bileşenleri ayırmamız lazım ki daha anlaşılabilir olsun. Hem sonradan okuyan kişi sen bilgisayar ile ne konuşmuşsun anlasın. Yalın olsun ya. Hatan mı var bu metotta? Çok aramasın şak diye bulsun. Alakasız, tekrar eden (redundant) şeyleri görmesin metot içinde. Zaman kıymetli. Ha bide alsın mesela o bildirim atan metodu, başka projede aynen kullansın. Sonuçta ister ürün bildirimi, ister alarm bildirimi ne fark eder ki!

Sınıf özeline dönelim mesela; bina ile ilgili bir sınıfınız var. Binada bulunan asansörün durumu, giriş kapısının kontrolü ve analitik amaçlı tuttuğunuz, kaç kişi girmiş-çıkmış kayıt ettiğiniz log sistemi var. Bina ile ilgili olan metotlarınızı bu sınıf içerisinde tutmak gerekir. Giriş kapısı için createResident, loginResident, elevatorStatus gibi 3 metot bu sınıf içinde olmalı. Çünkü bunlar binanın kendisi ile ilgili. Bina sınıfı kendi işini yapsın. Ama kullanıcılara mail atan ya da log yazan, writeLog veya sendEmail gibi fonksiyonların bu sınıf içinde olması demek, bina birden fazla, kendi ile alakasız işler yapıyor demek. Kendi ile ilgili olmayan verileri değiştiriyor demek. Bunların hepsini bir sınıfa doldurursanız kompleksite (complexity) artar, mesela hata arayan kişi daha büyük bir havuzda hata arayacak demektir. Yazık dimi? Valla arkanızdan gelen adam sizi çok iyi anmaz. “Ne pis muhabbetiniz varmış bilader, oku oku anlayamadık” der. Bir nevii nezaket bu işler.

Özetlersek;

  • Tek Sorumluluk Prensibi, her sınıfın veya methodun yalnızca bir işi yapması veya üzerinde çalıştığı veriyi değiştirmek için bir nedeni olması gerektiğini belirtir. Böyle buyurdular ben demedim :)
  • Tek Sorumluluk Prensibi, aynı değişiklik nedeni ile sınıfları veya metotları ayırmak için kullanılır.

Bu prensibe uygun geliştirilmiş projeye sonradan girişirseniz, dediğimiz gibi okunulabilirliliği, değiştirilebilirliği yüksek olduğu için ve gereksiz tekrarlanmadığı için çabucak adapte olur, iletişimi kolayca çözersiniz. Muhabbete hemen dahil olursunuz sanki hep ordaymışsınız gibi.

--

--