Ruby Sınıfları ve Modülleri: Kapsamlı Bir Programlama Rehberi
Ruby, nesne yönelimli programlamanın temel ilkelerini benimseyen dinamik bir dildir ve bu paradigmanın en önemli yapı taşları sınıflar ve modüllerdir. Bu iki yapı, kodun düzenlenmesinde, yeniden kullanılabilirliğin artırılmasında ve esnek, sürdürülebilir uygulamaların geliştirilmesinde kritik bir rol oynar. Bu rehberde, Ruby'deki sınıfların ve modüllerin ne olduğunu, nasıl kullanıldığını, aralarındaki farkları ve Ruby programlamasındaki önemlerini ayrıntılı bir şekilde inceleyeceğiz. Amacımız, bu temel kavramları derinlemesine anlayarak daha etkili ve modüler Ruby kodu yazmanıza yardımcı olmaktır.
Ruby Sınıfları: Nesnelerin Mimarları
Ruby'de her değer bir nesnedir ve her nesne belirli bir sınıfın örneğidir. Sınıflar, nesnelerin nasıl oluşturulacağını, hangi verilere (örnek değişkenler) sahip olacağını ve hangi davranışları (metotlar) sergileyeceğini tanımlayan şablonlardır. Bir sınıf, kendi nesnelerinin yapısını ve işlevselliğini belirleyen soyut bir "plan" veya "taslak" olarak düşünülebilir.
Sınıf Tanımlama ve Nesne Oluşturma
Bir sınıf, `class` anahtar kelimesi kullanılarak tanımlanır. Sınıf adları büyük harfle başlamalıdır (PascalCase geleneğine uygun olarak).
Yukarıdaki örnekte, `Musteri` sınıfı üç örnek değişkeni (`@ad`, `@soyad`, `@email`) ve üç örnek metot (`tam_ad`, `iletisim_bilgisi`, `kurumsal_mi?`) ile tanımlanmıştır. `@` ile başlayan değişkenler, o sınıfın her bir nesnesine ait olan örnek değişkenlerdir. Her `Musteri` nesnesi kendi `ad`, `soyad` ve `email` değerlerine sahip olacaktır.
Sınıf Değişkenleri ve Sınıf Metotları
Sınıflar ayrıca `@@` ile başlayan sınıf değişkenleri ve `self.` ile başlayan sınıf metotları içerebilir. Sınıf değişkenleri, o sınıfın tüm örnekleri tarafından paylaşılan verilerdir ve genellikle sınıf düzeyinde sayım veya genel yapılandırma bilgisi tutmak için kullanılır. Sınıf metotları ise doğrudan sınıf üzerinde çağrılır, nesneler üzerinde değil; sınıfın geneline ait işlemleri yapmak için idealdir.
Kalıtım (Inheritance): Kodun Yeniden Kullanılabilirliğini Artırma
Ruby'de sınıflar, diğer sınıflardan kalıtım alabilir. Bu mekanizma, bir alt sınıfın bir üst sınıfın tüm metotlarını ve örnek değişkenlerini devralmasını sağlar. Kalıtım, kod tekrarını azaltır, yeni sınıflar tanımlamayı kolaylaştırır ve hiyerarşik ilişkileri (örneğin, Genel `Hayvan` sınıfından özel `Kedi` veya `Köpek` sınıfları) modellemek için kullanılır. Bir sınıf, `<` operatörü kullanılarak başka bir sınıftan kalıtım alabilir. Ruby'de tekli kalıtım desteklenir, yani bir sınıf yalnızca tek bir üst sınıftan (ebeveyn sınıf) kalıtım alabilir.
`super` anahtar kelimesi, alt sınıftan üst sınıfın aynı isimli metodunu çağırmak için kullanılır. Bu, özellikle `initialize` metotlarında üst sınıfın başlatma mantığını korumak veya mevcut metotlara ek davranışlar eklemek için çok önemlidir. `super` argüman almadan çağrıldığında, alt sınıf metoduna gelen tüm argümanları otomatik olarak üst sınıf metoduna iletir.
Ruby Modülleri: Davranışları Gruplama ve Mixin Gücü
Modüller, metotları, sabitleri ve sınıf değişkenlerini gruplamak için kullanılan yapılardır. Sınıfların aksine, modüllerin doğrudan örnekleri oluşturulamaz (`new` metotları yoktur) ve modüllerden kalıtım alınamaz. Modüllerin temel amacı, kod tekrarını önlemek ve belirli davranışları sınıflara "karıştırmak" (mixin) için bir mekanizma sağlamaktır. Ruby'de çoklu kalıtım doğrudan desteklenmez, ancak modüllerin `include`, `extend` ve `prepend` mekanizmaları sayesinde sınıflara birden fazla kaynaktan davranış ekleme esnekliği sağlanır.
Modül Tanımlama
Bir modül, `module` anahtar kelimesi kullanılarak tanımlanır. Modül adları da büyük harfle başlamalıdır.
Modüllerin Kullanımı: Mixinler (`include`)
Modüllerin en güçlü kullanım alanlarından biri "mixin"lerdir. Bir modülü bir sınıfa `include` ederek, modüldeki tüm örnek metotlarını o sınıfın örneklerine ekleyebilirsiniz. Bu, bir sınıfın birden fazla kaynaktan (modülden) davranışları almasını sağlar. `include` edilen modül, sınıfın kalıtım zincirine bir üst sınıf gibi eklenir. Ruby, bir metodu ararken ilk olarak nesnenin kendi sınıfında, ardından `include` edilen modüllerde (tersten sıraya göre) ve en son üst sınıflarda arar.
extend vs. include: Metotların Uygulama Düzeyi
`include` bir modüldeki metotları sınıfın örnek metotları olarak eklerken, `extend` modüldeki metotları doğrudan sınıfın sınıf metotları olarak ekler. Yani, `extend` ile eklenen metotlar, sınıfın kendisi üzerinde çağrılır, o sınıfın nesneleri üzerinde değil.
`include` ve `extend` arasındaki fark, metotların hangi "seviyeye" (nesne örneği veya sınıfın kendisi) eklendiğidir. Bu, modüllerin esnekliğini ve farklı kullanım senaryolarına adapte olabilme yeteneğini gösterir.
prepend: Include'dan Bir Adım Önce
Ruby 2.0 ile tanıtılan `prepend` anahtar kelimesi, `include`'a benzer şekilde bir modülün metotlarını bir sınıfa ekler, ancak önemli bir farkla: `prepend` edilen modül, sınıfın kendisinden önce kalıtım zincirine eklenir. Bu, aynı isimde bir metot hem sınıfta hem de modülde tanımlıysa, modüldeki metodun öncelik kazanmasını sağlar ve metodun "takviye" edilmesini (örneğin, orijinal metodun etrafına ek davranış sarmalamak) mümkün kılar. Bu, AOP (Aspect-Oriented Programming) benzeri yaklaşımlar için oldukça kullanışlıdır.
Bu örnekte, `PerformansliGorevYoneticisi` sınıfı `ZamanlayiciModulu`'nu `prepend` ettiği için, `calistir_islem` metodu çağrıldığında ilk olarak `ZamanlayiciModulu`'ndaki `calistir_islem` metodu çalışır. Bu metot içindeki `super` çağrısı, `GorevYoneticisi` sınıfındaki orijinal `calistir_islem` metodunu tetikler. Bu, özellikle mevcut metotlara loglama, ölçüm, güvenlik katmanları veya ön/son işlemler eklemek için çok kullanışlıdır, çünkü orijinal metodun davranışını değiştirmeden etrafına yeni davranışlar sarmalamanıza olanak tanır.
Sınıflar ve Modüller Arasındaki Temel Farklar
Her ikisi de kodu organize etmek için kullanılsa da, sınıflar ve modüller farklı amaçlara hizmet eder ve belirli durumlar için daha uygunlardır:
Ruby'nin bu yapıları, Clean Code, DRY (Don't Repeat Yourself) ve SOLID prensipleri gibi modern yazılım mühendisliği yaklaşımlarını destekler. Özellikle Tek Sorumluluk Prensibi (Single Responsibility Principle) uygulamasında modüller çok yardımcı olur; bir sınıfın birden fazla sorumluluğu varsa, bu sorumlulukları ayrı modüllere bölerek ve sınıfa mixin olarak ekleyerek sınıfın temel sorumluluğunu korurken işlevselliğini genişletebilirsiniz.
Ruby Resmi Dökümantasyonu gibi kaynaklar, bu konuları daha derinlemesine keşfetmek ve ek örnekler görmek için harika başlangıç noktalarıdır.
Sonuç
Ruby'deki sınıflar ve modüller, kodun modülerliğini, yeniden kullanılabilirliğini ve sürdürülebilirliğini artıran güçlü ve esnek araçlardır. Sınıflar, nesnelerin yapısını ve davranışını tanımlayan temel blueprint'lerdir ve kalıtım yoluyla hiyerarşik ilişkileri modellemeye olanak tanır. Modüller ise, ortak davranışları gruplayarak ve mixin olarak sınıflara dahil edilerek çoklu kalıtıma benzer bir esneklik sağlar ve kodun daha organize ve okunabilir olmasına yardımcı olur. Bu iki yapıyı ustalıkla kullanmak, daha temiz, daha sürdürülebilir ve daha esnek Ruby uygulamaları yazmanın anahtarıdır. Her Ruby geliştiricisinin bu kavramları derinlemesine anlaması, dilin gücünden tam olarak yararlanabilmesi ve karmaşık sistemleri etkili bir şekilde tasarlayabilmesi için elzemdir.
Ruby, nesne yönelimli programlamanın temel ilkelerini benimseyen dinamik bir dildir ve bu paradigmanın en önemli yapı taşları sınıflar ve modüllerdir. Bu iki yapı, kodun düzenlenmesinde, yeniden kullanılabilirliğin artırılmasında ve esnek, sürdürülebilir uygulamaların geliştirilmesinde kritik bir rol oynar. Bu rehberde, Ruby'deki sınıfların ve modüllerin ne olduğunu, nasıl kullanıldığını, aralarındaki farkları ve Ruby programlamasındaki önemlerini ayrıntılı bir şekilde inceleyeceğiz. Amacımız, bu temel kavramları derinlemesine anlayarak daha etkili ve modüler Ruby kodu yazmanıza yardımcı olmaktır.
Ruby Sınıfları: Nesnelerin Mimarları
Ruby'de her değer bir nesnedir ve her nesne belirli bir sınıfın örneğidir. Sınıflar, nesnelerin nasıl oluşturulacağını, hangi verilere (örnek değişkenler) sahip olacağını ve hangi davranışları (metotlar) sergileyeceğini tanımlayan şablonlardır. Bir sınıf, kendi nesnelerinin yapısını ve işlevselliğini belirleyen soyut bir "plan" veya "taslak" olarak düşünülebilir.
Sınıf Tanımlama ve Nesne Oluşturma
Bir sınıf, `class` anahtar kelimesi kullanılarak tanımlanır. Sınıf adları büyük harfle başlamalıdır (PascalCase geleneğine uygun olarak).
Kod:
class Musteri
# initialize metodu, bir nesne oluşturulduğunda otomatik olarak çağrılır.
# Bu metod, nesnenin başlangıç durumunu belirlemek için kullanılır.
def initialize(ad, soyad, email)
@ad = ad # Örnek değişken: Her Musteri nesnesine özgü.
@soyad = soyad
@email = email
puts "Yeni bir Musteri nesnesi oluşturuldu: #{@ad} #{@soyad}"
end
# Örnek metot: Musteri nesnesinin bilgilerini dışarıya açar.
def tam_ad
"#{@ad} #{@soyad}"
end
# Örnek metot: Email adresini formatlı bir şekilde döner.
def iletisim_bilgisi
"Email: #{@email}"
end
# Örnek metot: Musterinin e-posta etki alanını kontrol eder.
def kurumsal_mi?
@email.end_with?("@kurumsal.com")
end
end
# Nesne oluşturma (instance alma):
# 'new' metodu, initialize metodunu çağırarak yeni bir nesne yaratır.
musteri1 = Musteri.new("Ali", "Can", "ali.can@example.com")
puts "Musteri Adı: #{musteri1.tam_ad}"
puts "#{musteri1.iletisim_bilgisi}"
puts "Kurumsal mı?: #{musteri1.kurumsal_mi?}"
musteri2 = Musteri.new("Ayşe", "Demir", "ayse.demir@kurumsal.com")
puts "Musteri Adı: #{musteri2.tam_ad}"
puts "#{musteri2.iletisim_bilgisi}"
puts "Kurumsal mı?: #{musteri2.kurumsal_mi?}"
Yukarıdaki örnekte, `Musteri` sınıfı üç örnek değişkeni (`@ad`, `@soyad`, `@email`) ve üç örnek metot (`tam_ad`, `iletisim_bilgisi`, `kurumsal_mi?`) ile tanımlanmıştır. `@` ile başlayan değişkenler, o sınıfın her bir nesnesine ait olan örnek değişkenlerdir. Her `Musteri` nesnesi kendi `ad`, `soyad` ve `email` değerlerine sahip olacaktır.
Sınıf Değişkenleri ve Sınıf Metotları
Sınıflar ayrıca `@@` ile başlayan sınıf değişkenleri ve `self.` ile başlayan sınıf metotları içerebilir. Sınıf değişkenleri, o sınıfın tüm örnekleri tarafından paylaşılan verilerdir ve genellikle sınıf düzeyinde sayım veya genel yapılandırma bilgisi tutmak için kullanılır. Sınıf metotları ise doğrudan sınıf üzerinde çağrılır, nesneler üzerinde değil; sınıfın geneline ait işlemleri yapmak için idealdir.
Kod:
class Urun
@@toplam_urun_sayisi = 0 # Sınıf değişkeni: Tüm Urun nesneleri arasında paylaşılır.
@@urun_kategorileri = [] # Sınıf değişkeni
def initialize(ad, fiyat, kategori)
@ad = ad
@fiyat = fiyat
@kategori = kategori
@@toplam_urun_sayisi += 1
@@urun_kategorileri << kategori unless @@urun_kategorileri.include?(kategori)
end
def bilgi
"#{@ad} (#{@kategori}) - #{@fiyat} TL"
end
# Sınıf metodu: Direkt Urun sınıfı üzerinden çağrılır.
def self.goster_toplam_urun_sayisi
"Sistemde toplam #{@@toplam_urun_sayisi} adet ürün bulunmaktadır."
end
# Sınıf metodu: Mevcut tüm ürün kategorilerini listeler.
def self.goster_urun_kategorileri
"Mevcut kategoriler: #{@@urun_kategorileri.join(', ')}"
end
end
Urun.new("Klavye", 350, "Elektronik")
Urun.new("Fare", 200, "Elektronik")
Urun.new("Defter", 50, "Kırtasiye")
Urun.new("Kalem", 20, "Kırtasiye")
puts Urun.goster_toplam_urun_sayisi # Sınıf metodu çağrısı
puts Urun.goster_urun_kategorileri # Sınıf metodu çağrısı
Kalıtım (Inheritance): Kodun Yeniden Kullanılabilirliğini Artırma
Ruby'de sınıflar, diğer sınıflardan kalıtım alabilir. Bu mekanizma, bir alt sınıfın bir üst sınıfın tüm metotlarını ve örnek değişkenlerini devralmasını sağlar. Kalıtım, kod tekrarını azaltır, yeni sınıflar tanımlamayı kolaylaştırır ve hiyerarşik ilişkileri (örneğin, Genel `Hayvan` sınıfından özel `Kedi` veya `Köpek` sınıfları) modellemek için kullanılır. Bir sınıf, `<` operatörü kullanılarak başka bir sınıftan kalıtım alabilir. Ruby'de tekli kalıtım desteklenir, yani bir sınıf yalnızca tek bir üst sınıftan (ebeveyn sınıf) kalıtım alabilir.
Kod:
class Hayvan
def initialize(isim)
@isim = isim
end
def ses_cikar
"#{@isim} ses çıkarıyor."
end
def beslen
"#{@isim} besleniyor."
end
end
class Kedi < Hayvan # Kedi sınıfı, Hayvan sınıfından kalıtım alır
def initialize(isim, tuy_rengi)
super(isim) # Üst sınıfın initialize metodunu çağırır
@tuy_rengi = tuy_rengi
end
def ses_cikar
"#{@isim} miyavlıyor! (Tüy rengi: #{@tuy_rengi})"
end
def tirnak_sivrit
"#{@isim} tırnaklarını sivritiyor."
end
end
class Kopek < Hayvan # Kopek sınıfı da Hayvan sınıfından kalıtım alır
def initialize(isim, irk)
super(isim)
@irk = irk
end
def ses_cikar
"#{@isim} havlıyor! (Irk: #{@irk})"
end
def egzersiz_yap
"#{@isim} egzersiz yapıyor."
end
end
kedi = Kedi.new("Mırnav", "Turuncu")
kopek = Kopek.new("Karabaş", "Sivas Kangalı")
puts kedi.ses_cikar
puts kedi.beslen
puts kedi.tirnak_sivrit
puts kopek.ses_cikar
puts kopek.beslen
puts kopek.egzersiz_yap
`super` anahtar kelimesi, alt sınıftan üst sınıfın aynı isimli metodunu çağırmak için kullanılır. Bu, özellikle `initialize` metotlarında üst sınıfın başlatma mantığını korumak veya mevcut metotlara ek davranışlar eklemek için çok önemlidir. `super` argüman almadan çağrıldığında, alt sınıf metoduna gelen tüm argümanları otomatik olarak üst sınıf metoduna iletir.
Ruby Modülleri: Davranışları Gruplama ve Mixin Gücü
Modüller, metotları, sabitleri ve sınıf değişkenlerini gruplamak için kullanılan yapılardır. Sınıfların aksine, modüllerin doğrudan örnekleri oluşturulamaz (`new` metotları yoktur) ve modüllerden kalıtım alınamaz. Modüllerin temel amacı, kod tekrarını önlemek ve belirli davranışları sınıflara "karıştırmak" (mixin) için bir mekanizma sağlamaktır. Ruby'de çoklu kalıtım doğrudan desteklenmez, ancak modüllerin `include`, `extend` ve `prepend` mekanizmaları sayesinde sınıflara birden fazla kaynaktan davranış ekleme esnekliği sağlanır.
Modül Tanımlama
Bir modül, `module` anahtar kelimesi kullanılarak tanımlanır. Modül adları da büyük harfle başlamalıdır.
Kod:
module Raporlama
# Örnek metot olarak kullanılacak
def rapor_olustur(veri)
puts "[RAPOR] #{Time.now}: #{self.class} nesnesi için rapor oluşturuluyor..."
puts "Veri: #{veri}"
puts "Rapor tamamlandı."
end
# Modül metodu (direkt modül üzerinden çağrılır)
def self.hata_logla(hata_mesaji)
$stderr.puts "[HATA] #{Time.now}: #{hata_mesaji}"
end
BASLIK_STILI = "---" # Sabitler
RAPOR_VERSIYON = "1.0.0"
end
# Modül metodu çağrısı
Raporlama.hata_logla("Veritabanı bağlantı hatası oluştu.")
puts "Raporlama Modülü Başlık Stili: #{Raporlama::BASLIK_STILI}"
puts "Raporlama Modülü Versiyonu: #{Raporlama::RAPOR_VERSIYON}"
Modüllerin Kullanımı: Mixinler (`include`)
Modüllerin en güçlü kullanım alanlarından biri "mixin"lerdir. Bir modülü bir sınıfa `include` ederek, modüldeki tüm örnek metotlarını o sınıfın örneklerine ekleyebilirsiniz. Bu, bir sınıfın birden fazla kaynaktan (modülden) davranışları almasını sağlar. `include` edilen modül, sınıfın kalıtım zincirine bir üst sınıf gibi eklenir. Ruby, bir metodu ararken ilk olarak nesnenin kendi sınıfında, ardından `include` edilen modüllerde (tersten sıraya göre) ve en son üst sınıflarda arar.
Kod:
module Karsilastirilabilir
def ==(other)
self.id == other.id
end
def <(other)
self.deger < other.deger
end
def >(other)
self.deger > other.deger
end
end
class Oge
include Karsilastirilabilir # Karsilastirilabilir modülünü Oge sınıfına karıştırır
attr_reader :id, :deger
def initialize(id, deger)
@id = id
@deger = deger
end
def to_s
"Oge (ID: #{@id}, Deger: #{@deger})"
end
end
nesne_a = Oge.new(1, 10)
nesne_b = Oge.new(2, 5)
nesne_c = Oge.new(1, 15) # Aynı ID, farklı değer
puts "#{nesne_a} == #{nesne_b} ? #{nesne_a == nesne_b}" # ID'leri farklı
puts "#{nesne_a} == #{nesne_c} ? #{nesne_a == nesne_c}" # ID'leri aynı
puts "#{nesne_a} < #{nesne_b} ? #{nesne_a < nesne_b}" # 10 < 5 ?
puts "#{nesne_a} > #{nesne_b} ? #{nesne_a > nesne_b}" # 10 > 5 ?
extend vs. include: Metotların Uygulama Düzeyi
`include` bir modüldeki metotları sınıfın örnek metotları olarak eklerken, `extend` modüldeki metotları doğrudan sınıfın sınıf metotları olarak ekler. Yani, `extend` ile eklenen metotlar, sınıfın kendisi üzerinde çağrılır, o sınıfın nesneleri üzerinde değil.
Kod:
module HesaplamaYardimcilari
def kare_al(sayi)
sayi * sayi
end
def kup_al(sayi)
sayi * sayi * sayi
end
def cift_mi?(sayi)
sayi.even?
end
end
class IslemlerOrnegi
include HesaplamaYardimcilari # kare_al, kup_al, cift_mi? örnek metotları olur
def ornek_islem(x)
puts "#{x}'in Karesi: #{kare_al(x)}"
puts "#{x}'in Küpü: #{kup_al(x)}"
puts "#{x} Çift mi?: #{cift_mi?(x)}"
end
end
class GenelHesaplayici
extend HesaplamaYardimcilari # kare_al, kup_al, cift_mi? sınıf metotları olur
end
islem_objesi = IslemlerOrnegi.new
islem_objesi.ornek_islem(5)
# islem_objesi.kare_al(4) # Bu da çalışır, çünkü instance metot
puts "\nGenelHesaplayici sınıf metotları ile:"
puts "8'in Karesi: #{GenelHesaplayici.kare_al(8)}" # extend edildiği için sınıf metodu olarak çağrılır
puts "3'ün Küpü: #{GenelHesaplayici.kup_al(3)}"
puts "10 Çift mi?: #{GenelHesaplayici.cift_mi?(10)}"
# GenelHesaplayici.new.kare_al(2) # Bu çalışmaz, çünkü instance metot değil
`include` ve `extend` arasındaki fark, metotların hangi "seviyeye" (nesne örneği veya sınıfın kendisi) eklendiğidir. Bu, modüllerin esnekliğini ve farklı kullanım senaryolarına adapte olabilme yeteneğini gösterir.
prepend: Include'dan Bir Adım Önce
Ruby 2.0 ile tanıtılan `prepend` anahtar kelimesi, `include`'a benzer şekilde bir modülün metotlarını bir sınıfa ekler, ancak önemli bir farkla: `prepend` edilen modül, sınıfın kendisinden önce kalıtım zincirine eklenir. Bu, aynı isimde bir metot hem sınıfta hem de modülde tanımlıysa, modüldeki metodun öncelik kazanmasını sağlar ve metodun "takviye" edilmesini (örneğin, orijinal metodun etrafına ek davranış sarmalamak) mümkün kılar. Bu, AOP (Aspect-Oriented Programming) benzeri yaklaşımlar için oldukça kullanışlıdır.
Kod:
module ZamanlayiciModulu
def calistir_islem(islem_adi)
baslangic = Time.now
puts "[ZAMANLAYICI] Islem basliyor: '#{islem_adi}'"
super(islem_adi) # Orijinal calistir_islem metodunu çağırır
bitis = Time.now
gecen_sure = bitis - baslangic
puts "[ZAMANLAYICI] Islem '#{islem_adi}' bitti. Süre: #{'%.4f' % gecen_sure} saniye."
end
end
class GorevYoneticisi
def calistir_islem(islem_adi)
puts "Gerçekleştirilen islem: #{islem_adi}"
sleep(rand * 0.5) # Rastgele bir süre bekleme taklidi
end
end
class PerformansliGorevYoneticisi < GorevYoneticisi
prepend ZamanlayiciModulu # ZamanlayiciModulu, GorevYoneticisi'nden önce gelir
end
puts "Normal görev yöneticisi:"
normal_yonetici = GorevYoneticisi.new
normal_yonetici.calistir_islem("Veri Isleme")
puts "\nPerformanslı görev yöneticisi (prepend ile):"
performansli_yonetici = PerformansliGorevYoneticisi.new
performansli_yonetici.calistir_islem("Rapor Oluşturma")
performansli_yonetici.calistir_islem("Ağ Iletişimi")
Bu örnekte, `PerformansliGorevYoneticisi` sınıfı `ZamanlayiciModulu`'nu `prepend` ettiği için, `calistir_islem` metodu çağrıldığında ilk olarak `ZamanlayiciModulu`'ndaki `calistir_islem` metodu çalışır. Bu metot içindeki `super` çağrısı, `GorevYoneticisi` sınıfındaki orijinal `calistir_islem` metodunu tetikler. Bu, özellikle mevcut metotlara loglama, ölçüm, güvenlik katmanları veya ön/son işlemler eklemek için çok kullanışlıdır, çünkü orijinal metodun davranışını değiştirmeden etrafına yeni davranışlar sarmalamanıza olanak tanır.
Sınıflar ve Modüller Arasındaki Temel Farklar
Her ikisi de kodu organize etmek için kullanılsa da, sınıflar ve modüller farklı amaçlara hizmet eder ve belirli durumlar için daha uygunlardır:
- Örnek Oluşturma: Sınıfların örnekleri (`new` metodu ile) oluşturulabilir ve bu örnekler kendi durumlarına (örnek değişkenler) sahiptir. Modüllerin ise doğrudan örnekleri oluşturulamaz; onlar daha çok bir davranış koleksiyonu gibidir.
- Kalıtım: Sınıflar birbirlerinden kalıtım alabilir (tekli kalıtım desteklenir). Modüller ise kalıtım almaz veya veremezler, ancak sınıflara `include`, `extend` veya `prepend` edilerek davranışlarını paylaşırlar.
- Amaç: Sınıflar, belirli bir türdeki nesneleri ve onların durumlarını (veri) ve davranışlarını (metotlar) tanımlamak için kullanılır. Bir sınıf, "ne olduğu" (varlık) sorusuna cevap verir. Modüller ise genellikle davranışları veya yardımcı metotları gruplamak ve bu davranışları birden fazla sınıfa "karıştırmak" (mixin) için kullanılır. Bir modül, "ne yapabileceği" (yetkinlik) sorusuna cevap verir.
- Esneklik ve Çoklu Kalıtım: Modüller, Ruby'nin tekli kalıtım sınırlamasını aşarak, bir sınıfa birden fazla kaynaktan (modülden) davranış ekleme esnekliği sunar. Bu, birçok dildeki çoklu kalıtıma benzer bir yetenek sağlar ancak kalıtım hiyerarşisinin karmaşıklığı olmadan.
- Ad Alanı (Namespace): Modüller, sabitleri ve sınıfları gruplamak ve ad çakışmalarını önlemek için bir ad alanı (namespace) görevi de görebilir. Örneğin, `Math` modülü, matematiksel sabitleri (`Math:
I`) ve metotları (`Math.sqrt`) barındırır.
"Ruby'de her şey bir nesnedir. Her nesnenin bir sınıfı vardır ve bu sınıflar, nesnelerin nasıl davranacağını tanımlar. Modüller ise bu davranış setlerini, sınıflar arasında tekrarı önleyecek şekilde paylaşmanın zarif bir yoludur."
Ruby'nin bu yapıları, Clean Code, DRY (Don't Repeat Yourself) ve SOLID prensipleri gibi modern yazılım mühendisliği yaklaşımlarını destekler. Özellikle Tek Sorumluluk Prensibi (Single Responsibility Principle) uygulamasında modüller çok yardımcı olur; bir sınıfın birden fazla sorumluluğu varsa, bu sorumlulukları ayrı modüllere bölerek ve sınıfa mixin olarak ekleyerek sınıfın temel sorumluluğunu korurken işlevselliğini genişletebilirsiniz.
Ruby Resmi Dökümantasyonu gibi kaynaklar, bu konuları daha derinlemesine keşfetmek ve ek örnekler görmek için harika başlangıç noktalarıdır.
Sonuç
Ruby'deki sınıflar ve modüller, kodun modülerliğini, yeniden kullanılabilirliğini ve sürdürülebilirliğini artıran güçlü ve esnek araçlardır. Sınıflar, nesnelerin yapısını ve davranışını tanımlayan temel blueprint'lerdir ve kalıtım yoluyla hiyerarşik ilişkileri modellemeye olanak tanır. Modüller ise, ortak davranışları gruplayarak ve mixin olarak sınıflara dahil edilerek çoklu kalıtıma benzer bir esneklik sağlar ve kodun daha organize ve okunabilir olmasına yardımcı olur. Bu iki yapıyı ustalıkla kullanmak, daha temiz, daha sürdürülebilir ve daha esnek Ruby uygulamaları yazmanın anahtarıdır. Her Ruby geliştiricisinin bu kavramları derinlemesine anlaması, dilin gücünden tam olarak yararlanabilmesi ve karmaşık sistemleri etkili bir şekilde tasarlayabilmesi için elzemdir.