Nesne Yönelimli Programlama (NYP) dünyasının temel ilkelerinden biri olan enkapsülasyon, bir nesnenin iç durumunu (verisini) dış dünyadan saklama ve bu verilere erişimi sınırlama pratiğidir. Bu, hem veri bütünlüğünü korumak hem de kodun modülerliğini ve bakımını kolaylaştırmak için hayati öneme sahiptir. Ruby, diğer birçok NYP dilinden farklı bir yaklaşıma sahip olsa da, etkili bir kapsülleme mekanizması sunar.
Kapsüllemenin Amacı: Bir nesnenin iç detaylarını, bu nesneyi kullanan diğer kodlardan ayırmaktır. Böylece, nesnenin iç yapısında yapılan değişiklikler, onu kullanan dış kodu etkilemez. Bu, yazılım sistemlerinin daha kararlı, daha esnek ve daha kolay anlaşılır olmasını sağlar. Ruby'de kapsülleme, temel olarak metot erişim seviyeleri aracılığıyla ve geleneksel olarak instance değişkenlerini doğrudan manipüle etmek yerine erişimci metotları (accessor methods) kullanarak uygulanır.
Ruby'de Metot Erişim Seviyeleri:
Ruby'de üç ana metot erişim seviyesi bulunur:
1. Public Metotlar: Varsayılan erişim seviyesidir. Bir sınıfın public metotları, sınıfın herhangi bir örneği üzerinden dışarıdan çağrılabilir. Bunlar, sınıfın dış dünyaya sunduğu arayüzü oluşturur.
Yukarıdaki örnekte `kendini_tanit` metodu public'tir ve `Kisi` sınıfının bir örneği üzerinden rahatlıkla erişilebilir.
2. Protected Metotlar: Bu metotlar, sadece tanımlandıkları sınıfın veya alt sınıflarının örnekleri tarafından çağrılabilir. Yani, aynı sınıf veya bir alt sınıfın diğer örnekleri tarafından erişilebilirler, ancak sınıf hiyerarşisi dışındaki nesneler tarafından erişilemezler. Bu, benzer türdeki nesneler arasında işbirliği yaparken kullanışlıdır.
`bakiye_ekle` metodu `protected` olduğu için sadece `Hesap` sınıfının veya onun alt sınıflarının başka bir örneği üzerinden çağrılabilir. Doğrudan `hesap2.bakiye_ekle(50)` şeklinde dışarıdan çağırmaya çalıştığımızda hata alırız.
3. Private Metotlar: Bir sınıfın private metotları sadece sınıfın kendi örneği içinde çağrılabilir ve bu çağrılar açık bir alıcı (`self.metot` gibi) içermemelidir. Genellikle sınıfın iç işleyişinde kullanılan yardımcı metotlar için kullanılırlar ve dış dünyaya maruz kalmamalıdırlar. Bu, en sıkı kapsülleme seviyesidir.
`calistir_motor` ve `dur` metotları `private` olduğu için sadece `sur` metodu gibi sınıfın kendi içinden çağrılabilirler. Doğrudan bir nesne üzerinden çağrılamazlar.
Instance Değişkenleri ve Kapsülleme:
Ruby'de instance değişkenleri (`@degisken`) tamamen özel değildir; aslında sınıfın dışından `instance_variable_get` veya `instance_variable_set` gibi metotlarla erişilebilirler. Ancak bu, iyi bir pratik değildir ve genellikle kapsülleme ilkesini çiğner. Ruby geleneği, instance değişkenlerine doğrudan erişmek yerine erişimci metotları kullanmayı teşvik eder. Bu metotlar, verinin nasıl okunduğu veya yazıldığı üzerinde kontrol sahibi olmanızı sağlar.
Erişimci Metotları (Accessors):
Ruby, instance değişkenlerine okuma ve yazma metotları oluşturmak için kolaylıklar sunar:
* `attr_reader :degisken_adi`: Sadece okuma (getter) metodu oluşturur.
* `attr_writer :degisken_adi`: Sadece yazma (setter) metodu oluşturur.
* `attr_accessor :degisken_adi`: Hem okuma hem de yazma (getter ve setter) metotları oluşturur.
`attr_accessor` kullanımı, özellikle Rails gibi framework'lerde sıkça görülür, ancak kapsülleme açısından doğrudan `@degisken`e erişim yerine kontrollü bir arayüz sunar.
Kapsüllemenin Faydaları Özeti:
Kapsülleme ilkesini benimsemek, yazılım geliştirmede birçok avantaj sağlar:
Ruby'deki Esneklik ve İzin Verilen İhlaller (Dikkat!):
Ruby, dinamik ve esnek bir dildir ve çok nadir durumlarda kapsülleme kurallarını bypass etmeye olanak tanıyan metotlar sunar. Örneğin, `send`, `public_send`, `instance_variable_get`, `instance_variable_set` gibi metotlar, normalde erişilemeyen metotlara veya instance değişkenlerine erişmenizi sağlayabilir. Ancak bu metotların bilinçsizce kullanılması kapsüllemeyi tamamen bozar, kodun anlaşılırlığını ve bakımını zorlaştırır. Genellikle, bu tür metotlar metaprogramlama veya çok özel hata ayıklama senaryoları için ayrılmıştır ve normal uygulama geliştirmede kesinlikle önerilmez.
Örneğin, bir private metodu `send` ile çağırmak mümkündür:
Bu tür kullanımlar, Ruby'nin ne kadar esnek olabildiğini gösterse de, tasarım prensiplerine sadık kalmak ve kapsüllemeyi korumak her zaman tercih edilen yaklaşımdır.
Sonuç:
Ruby, metot erişim seviyeleri ve erişimci metotlar aracılığıyla güçlü bir kapsülleme mekanizması sunar. Public metotlar bir nesnenin dış dünyaya açılan kapısıyken, protected ve private metotlar iç işleyişi gizlemek ve veri bütünlüğünü sağlamak için kullanılır. İyi tasarlanmış bir Ruby uygulamasında, instance değişkenlerine doğrudan erişmekten kaçınılmalı ve bunun yerine erişimci metotlar kullanılmalıdır. Kapsülleme, sadece bir dil özelliği değil, aynı zamanda daha sağlam, anlaşılır ve sürdürülebilir yazılımlar geliştirmek için bir tasarım felsefesidir. Bu ilke, Ruby'nin dinamik yapısına rağmen, kod kalitesini artırmak için vazgeçilmezdir.
Ruby'nin metot erişim seviyeleri hakkında daha fazla bilgiye resmi Ruby dökümantasyonundan ulaşabilirsiniz.
Kapsüllemenin Amacı: Bir nesnenin iç detaylarını, bu nesneyi kullanan diğer kodlardan ayırmaktır. Böylece, nesnenin iç yapısında yapılan değişiklikler, onu kullanan dış kodu etkilemez. Bu, yazılım sistemlerinin daha kararlı, daha esnek ve daha kolay anlaşılır olmasını sağlar. Ruby'de kapsülleme, temel olarak metot erişim seviyeleri aracılığıyla ve geleneksel olarak instance değişkenlerini doğrudan manipüle etmek yerine erişimci metotları (accessor methods) kullanarak uygulanır.
Ruby'de Metot Erişim Seviyeleri:
Ruby'de üç ana metot erişim seviyesi bulunur:
1. Public Metotlar: Varsayılan erişim seviyesidir. Bir sınıfın public metotları, sınıfın herhangi bir örneği üzerinden dışarıdan çağrılabilir. Bunlar, sınıfın dış dünyaya sunduğu arayüzü oluşturur.
Kod:
class Kisi
def initialize(ad, yas)
@ad = ad
@yas = yas
end
def kendini_tanit
"Merhaba, ben #{@ad} ve #{@yas} yaşındayım."
end
end
kisi = Kisi.new("Ali", 30)
puts kisi.kendini_tanit # Public metot çağrısı
# Çıktı: Merhaba, ben Ali ve 30 yaşındayım.
2. Protected Metotlar: Bu metotlar, sadece tanımlandıkları sınıfın veya alt sınıflarının örnekleri tarafından çağrılabilir. Yani, aynı sınıf veya bir alt sınıfın diğer örnekleri tarafından erişilebilirler, ancak sınıf hiyerarşisi dışındaki nesneler tarafından erişilemezler. Bu, benzer türdeki nesneler arasında işbirliği yaparken kullanışlıdır.
Kod:
class Hesap
def initialize(bakiye)
@bakiye = bakiye
end
def transfer(hedef_hesap, miktar)
if yeterli_bakiye?(miktar)
@bakiye -= miktar
hedef_hesap.bakiye_ekle(miktar) # Aynı sınıfın başka bir örneği üzerinde protected metot çağrısı
"Transfer başarılı: #{miktar} TL."
else
"Yetersiz bakiye."
end
end
protected
def bakiye_ekle(miktar)
@bakiye += miktar
end
private # Not: Protected'dan sonra private metot tanımlayabiliriz.
def yeterli_bakiye?(miktar)
@bakiye >= miktar
end
end
hesap1 = Hesap.new(1000)
hesap2 = Hesap.new(500)
puts hesap1.transfer(hesap2, 200)
# puts hesap2.bakiye_ekle(50) # Hata verir: protected method `bakiye_ekle' called for #<Hesap:...>
3. Private Metotlar: Bir sınıfın private metotları sadece sınıfın kendi örneği içinde çağrılabilir ve bu çağrılar açık bir alıcı (`self.metot` gibi) içermemelidir. Genellikle sınıfın iç işleyişinde kullanılan yardımcı metotlar için kullanılırlar ve dış dünyaya maruz kalmamalıdırlar. Bu, en sıkı kapsülleme seviyesidir.
Kod:
class Araba
def initialize(hiz)
@hiz = hiz
end
def sur
calistir_motor # 'self.' olmadan çağrılmalı
puts "Araba hızlanıyor... Mevcut hız: #{@hiz} km/s."
dur
end
private
def calistir_motor
puts "Motor çalıştırılıyor."
end
def dur
puts "Araba durduruluyor."
end
end
araba = Araba.new(120)
araba.sur
# araba.calistir_motor # Hata verir: private method `calistir_motor' called for #<Araba:...>
Instance Değişkenleri ve Kapsülleme:
Ruby'de instance değişkenleri (`@degisken`) tamamen özel değildir; aslında sınıfın dışından `instance_variable_get` veya `instance_variable_set` gibi metotlarla erişilebilirler. Ancak bu, iyi bir pratik değildir ve genellikle kapsülleme ilkesini çiğner. Ruby geleneği, instance değişkenlerine doğrudan erişmek yerine erişimci metotları kullanmayı teşvik eder. Bu metotlar, verinin nasıl okunduğu veya yazıldığı üzerinde kontrol sahibi olmanızı sağlar.
Erişimci Metotları (Accessors):
Ruby, instance değişkenlerine okuma ve yazma metotları oluşturmak için kolaylıklar sunar:
* `attr_reader :degisken_adi`: Sadece okuma (getter) metodu oluşturur.
* `attr_writer :degisken_adi`: Sadece yazma (setter) metodu oluşturur.
* `attr_accessor :degisken_adi`: Hem okuma hem de yazma (getter ve setter) metotları oluşturur.
Kod:
class Urun
attr_reader :isim # Okuma metodu: urun.isim
attr_writer :fiyat # Yazma metodu: urun.fiyat = 100
attr_accessor :stok # Hem okuma hem yazma: urun.stok ve urun.stok = 50
def initialize(isim, fiyat, stok)
@isim = isim
@fiyat = fiyat
@stok = stok
end
def urun_bilgisi
"Ürün: #{isim}, Fiyat: #{fiyat_goster}, Stok: #{stok}"
end
private
def fiyat_goster
# İçeride özel bir formatlama yapabiliriz
"%.2f TL" % @fiyat
end
end
urun = Urun.new("Klavye", 250.75, 10)
puts urun.isim
# Çıktı: Klavye
urun.fiyat = 270.00
# puts urun.fiyat # hata verir çünkü sadece yazıcı tanımlandı, okuyucu yok
puts urun.stok
# Çıktı: 10
urun.stok = 15
puts urun.stok
# Çıktı: 15
puts urun.urun_bilgisi
# Çıktı: Ürün: Klavye, Fiyat: 270.00 TL, Stok: 15
Kapsüllemenin Faydaları Özeti:
Kapsülleme ilkesini benimsemek, yazılım geliştirmede birçok avantaj sağlar:
- Veri Bütünlüğü: Nesnenin iç verilerinin doğrudan ve kontrolsüz manipülasyonunu engeller, bu da verinin her zaman geçerli bir durumda kalmasını sağlar.
- Modülerlik: Her nesne, kendi sorumluluklarını üstlenir ve iç detaylarını dışarıdan gizler. Bu, bağımlılıkları azaltır ve kodun farklı bölümlerinin birbirinden izole edilmesini sağlar.
- Esneklik ve Bakım Kolaylığı: Bir nesnenin iç uygulaması değişse bile, dışarıya sunulan arayüz aynı kaldığı sürece, o nesneyi kullanan diğer kodların etkilenmesini önler. Bu, refactoring (yeniden yapılandırma) ve bakım süreçlerini kolaylaştırır.
- Kodun Anlaşılırlığı: Bir nesnenin nasıl kullanılacağı, sadece public arayüzüne bakılarak anlaşılabilir. İç detaylara takılmaya gerek kalmaz.
- Geliştirici Sorumlulukları: Kapsülleme, bir sınıfın geliştiricisinin, o sınıfı kullanan diğer geliştiricilerin nesneyi doğru ve güvenli bir şekilde kullanmasını sağlamasına yardımcı olur.
"Kapsülleme, programlama dünyasında bir nesnenin iç yapısını 'kara kutu' gibi yapmaktır; dışarıdan sadece belirli giriş/çıkış noktalarına erişilir, içindeki karmaşık mekanizmalar gizlenir."
Ruby'deki Esneklik ve İzin Verilen İhlaller (Dikkat!):
Ruby, dinamik ve esnek bir dildir ve çok nadir durumlarda kapsülleme kurallarını bypass etmeye olanak tanıyan metotlar sunar. Örneğin, `send`, `public_send`, `instance_variable_get`, `instance_variable_set` gibi metotlar, normalde erişilemeyen metotlara veya instance değişkenlerine erişmenizi sağlayabilir. Ancak bu metotların bilinçsizce kullanılması kapsüllemeyi tamamen bozar, kodun anlaşılırlığını ve bakımını zorlaştırır. Genellikle, bu tür metotlar metaprogramlama veya çok özel hata ayıklama senaryoları için ayrılmıştır ve normal uygulama geliştirmede kesinlikle önerilmez.
Örneğin, bir private metodu `send` ile çağırmak mümkündür:
Kod:
class Test
private
def gizli_metot
"Gizli bilgi!"
end
end
t = Test.new
# puts t.gizli_metot # Hata!
puts t.send(:gizli_metot) # Çalışır!
# Çıktı: Gizli bilgi!
Sonuç:
Ruby, metot erişim seviyeleri ve erişimci metotlar aracılığıyla güçlü bir kapsülleme mekanizması sunar. Public metotlar bir nesnenin dış dünyaya açılan kapısıyken, protected ve private metotlar iç işleyişi gizlemek ve veri bütünlüğünü sağlamak için kullanılır. İyi tasarlanmış bir Ruby uygulamasında, instance değişkenlerine doğrudan erişmekten kaçınılmalı ve bunun yerine erişimci metotlar kullanılmalıdır. Kapsülleme, sadece bir dil özelliği değil, aynı zamanda daha sağlam, anlaşılır ve sürdürülebilir yazılımlar geliştirmek için bir tasarım felsefesidir. Bu ilke, Ruby'nin dinamik yapısına rağmen, kod kalitesini artırmak için vazgeçilmezdir.
Ruby'nin metot erişim seviyeleri hakkında daha fazla bilgiye resmi Ruby dökümantasyonundan ulaşabilirsiniz.