Ruby'nin Yöntem Arama Mekanizması: Nesnelerin Yöntemlerini Nasıl Bulduğu

Ruby'nin Yöntem Arama Mekanizması: Nesnelerin Yöntemlerini Nasıl Bulduğu
audio-thumbnail
Ruby'nin Yöntem Arama Mekanizması: Nesnelerin Yöntemlerini Nasıl Bulduğu
0:00
/0

Ruby'nin Yöntem Arama Mekanizması: Nesnelerin Yöntemlerini Nasıl Bulduğu

Ruby, "her şey bir nesnedir" sloganıyla bilinen ve çok sayıda geliştirici tarafından sevilen bir programlama dilidir. Ancak bu dilin arkasındaki gerçek yapısı, büyük ölçüde kavramsal bir hileyle örtülüdür. Özellikle nesnelerin yöntemleri nasıl bulduğu konusu, birçok geliştirici için bir "açıklayıcı" olabilir. Çünkü Ruby’de nesnelerin yöntemlerini doğrudan taşımadığı, yöntemlerin ise sınıflar ve gizli bir yapı olan eigenclass (tekil sınıf) üzerinde saklandığı gerçeği, dilin yaygın olarak anlaşılan temel mantığıyla tamamen çelişir.

Bu yazıda, Ruby’nin yöntem arama mekanizmasını geliştirici perspektifinden açıklamak amacıyla, nesnelerin yöntemleri nasıl bulduğu, tekil yöntemlerin (singleton methods) nasıl çalıştığı ve hatta "sınıf yöntemleri"nin gerçekte ne olduğunu inceleyeceğiz.


İlk Şaşkınlık: Nesneler Durumu, Sınıflar Yöntemleri Saklar

Ruby’de bir nesne, yöntemlerini doğrudan içermeyebilir. Bu, biraz şaşırtıcı olabilir, çünkü herhangi bir user.greet çağrısını düşünürsek, nesnenin içinde bir greet yöntemi olması gerektiğini düşünebiliriz.

Ancak CRuby (MRI)’de nesnelerin iç yapısına baktığımızda şu görülür:

  • Her nesne, örnek değişkenlerini (örneğin @name, @email) tutar.
  • Ayrıca bir klass adlı işaretçi (pointer) içerir, bu işaretçi nesnenin sınıfına işaret eder.

Yani, nesne içinde bir yöntem tablosu (method table) yoktur. Yöntemler, nesnenin sınıfında saklanır.

Bir örnek ile açıklayalım:

class User
  def initialize(name, email)
    @name = name
    @email = email
  end

  def greet
    "Hello, I am #{@name}"
  end
end

user = User.new("Alice", "[email protected]")

user nesnesi oluşturulduğunda, Ruby bir RObject oluşturur. Bu nesne içinde:

  • @name ve @email gibi örnek değişkenleri barındırır.
  • klass işaretçisi, User sınıfına işaret eder.

greet yöntemi user nesnesinin içinde değildir. Tam tersine, User sınıfının içindeki bir m_tbl (method table) adlı harita içinde saklanır. Bu haritada :greet simgesi, yöntemin derlenmiş bytecode’una eşlenmiştir.

İşte bu yapı sayesinde, yüz binlerce User nesnesi oluşturulduğunda, tüm nesneler aynı metod tablosunu paylaşır. Bu, hem bellek tasarrufu hem de performans açısından büyük bir avantajdır.


Tekil Yöntemler (Singleton Methods): Gizli Bir Sınıfın Sırrı

Ruby’de, bir tek nesneye özel bir yöntem tanımlayabilmemiz mümkündür. Bu tür yöntemlerde, "tekil yöntem" (singleton method) adı verilir.

İşte bir örnek:

str1 = "hello"
str2 = "world"

def str1.shout
  upcase + "!!!"
end

str1.shout  #=> "HELLO!!!"
str2.shout  #=> NoMethodError

Burada str1 nesnesine özel bir shout yöntemi tanımlanmıştır. Ama str2 nesnesi bu yönteme sahip değildir.

Peki bu yöntem nerede saklanır? Nesneler yöntemleri tutmadığına göre, bu yöntem str1 içinde olamaz. Aynı zamanda String sınıfı içinde de olamaz, çünkü o zaman str2 de bu yöntemi kullanırdı.

Cevap: Ruby, bu yöntemler için gizli bir sınıf oluşturur. Bu sınıfa eigenclass (tekil sınıf) denir.

Ruby, str1 nesnesi üzerinde bir yöntem tanımlandığında:

  1. Gizli bir sınıfla (#<Class:str1>) karşılaşıyor.
  2. str1 nesnesinin klass işaretçisini bu yeni eigenclassa yönlendiriyor.
  3. Bu yeni sınıfa shout yöntemini ekliyor.
  4. eigenclassin üst sınıfı (superclass) ise String sınıfıdır.

Sonuç olarak, str1.shout çağrısı yapılırken Ruby:

  1. str1 nesnesinin klass işaretçisini takip eder.
  2. #<Class:str1> sınıfına girer.
  3. :shout yöntemini bulur ve çalıştırır.

Eğer str1.upcase çağrısı yapılırsa, Ruby #<Class:str1> içinde bu yöntemi bulamaz, sonra String sınıfına geçerek upcase metodunu bulur ve çalıştırır.

Bu süreç, Ruby’deki döngüsel yöntem arama mantığının temelini oluşturur.


"Sınıf Yöntemleri" Gerçekten Var Mı?

Geliştiriciler arasında yaygın bir kavram olan "sınıf yöntemi" (class method), Ruby’de bir tekil yöntem olarak görülür. Ancak bir "sınıf yöntem" tanımlarken:

class Post
  def self.recent
    # son yazıları getir
  end
end

Bu kodda, self.recent metodunu yazarken, aslında Post nesnesi üzerinde bir tekil yöntem tanımlıyoruz.

Neden?

Çünkü Post bir nesnedir. Ruby’de her şey bir nesnedir. Post bir Class sınıfının bir örneğidir:

Post.class #=> Class

Yani Post bir nesne, sadece bir sınıf nesnesidir. Bu nedenle def self.recent yazmak, def Post.recent yazmakla aynı şeydir. Her iki durumda da, Post nesnesinin özel bir eigenclassı (yani #<Class:Post>) oluşturulur ve bu sınıfa :recent yöntemi eklenir.

Bu yüzden "sınıf yöntemi" diye bir şey yoktur. Ruby’de sadece tekil yöntemler vardır. Bu yöntemler, sınıf nesnelerinin eigenclass’larında saklanır.


Yöntem Arama Zıpkınları: Yöntem Arama Zinciri

Ruby, bir yöntem çağrısı yapıldığında, hangi yöntemin çağrılacağını belirli bir sıraya göre arar. Bu sıraya ancestors zinciri (kaynak zinciri) denir.

receiver.message çağrısı yapıldığında, Ruby aşağıdaki adımları takip eder:

  1. Alıcı nesnenin eigenclass’ı (tekil sınıfı): Eğer nesne özel bir yöntem tanımlamışsa, bu yöntem önce aranır.
  2. eigenclass’a prepend edilmiş modüller: Eğer eigenclass’a prepend ile modüller eklenmişse, bu modüllerdeki yöntemler aranır.
  3. Alıcı nesnenin sınıfı: Eğer yukarıdaki adımlarda yöntem bulunamazsa, alıcı nesnenin sınıfına geçilir.
  4. Sınıfın modülleri ve üst sınıflar: Sınıfın include ile eklenen modüller ve superclass ile devralınan yöntemler aranır.

Bu sırayı kavramak, Ruby’deki metod örtme (method overriding) ve modül ekleme davranışlarını anlamak için çok önemlidir.


Özet: Ruby’de Yöntem Arama Nasıl Çalışır?

  • Nesneler yöntemleri tutmaz. Yöntemler, nesnelerin sınıflarında saklanır.
  • Tekil yöntemler (singleton methods), nesne başına özel bir eigenclass (tekil sınıf) oluşturarak saklanır.
  • "Sınıf yöntemleri" aslında sınıf nesnesinin eigenclass’ında tanımlanan tekil yöntemlerdir.
  • Ruby, bir yöntem araması yaptığında, eigenclass → prepend modüller → sınıf → üst sınıflar sırasıyla arar.
  • Bu yapı, hem bellek tasarrufu hem de esnek bir metod arama mekanizması sağlar.

Geliştirici Olarak Neden Bu Kavramlar Önemlidir?

Ruby’de bu yapıyı anlamak, sadece teknik bir bilgi değil, aynı zamanda dildeki mantığı ve nesne yönelimli tasarımın esnekliğini anlamamızı sağlar. Eğer bir nesneye özel bir davranış eklemek istiyorsanız, eigenclass’ı kullanarak singleton method tanımlayabilirsiniz. Aynı zamanda, bir sınıfın davranışını değiştirmek istiyorsanız, include ve prepend ile modülleri ekleyebilirsiniz.

Ruby’de "her şey bir nesnedir" dediğimizde, bu ifade sadece bir slogan değil, aynı zamanda dilin tüm yapısını açıklamaktadır. Yöntem arama mekanizması, bu yapıyı temel alan bir deterministik ve tutarlı sistemdir.

Böylece, Ruby’de nesnelerin yöntemlerini nasıl bulduğu gerçeği, sadece bir bilgi değil, aynı zamanda dilin gücünü ve esnekliğini anlamanın bir kapısıdır.