---
title: 'Yöntem Arama Zinciri'
source: 'https://academia.sh/tr/kurslar/ruby-nesne-yonelimi/yontem-arama-zinciri'
course: 'Nesne Yönelimi ve Metaprogramlama'
language: tr
updated: '2026-10-21T05:03:42+00:00'
license: 'CC BY-SA 4.0'
objectives:
    - 'Bir yöntem çağrısının ata zincirinde soldan sağa arandığını ancestors ve method(...).owner ile gösterir'
    - 'prepend ile eklenen modülün sınıfın önüne geçtiğini ve super ile sınıfa indiğini ölçer'
    - 'Sınıf yöntemlerinin tekil sınıf zincirinden geldiğini ölçer'
    - 'Tek bir nesneye eklenen tekil yöntemin zincirin başına girdiğini ve diğer nesneleri etkilemediğini gösterir'
---

# Yöntem Arama Zinciri

Bir ileti alıcıya ulaşınca yöntemin ata zincirinde nasıl arandığı; önüne eklenen modül, super_method, zincirin sonu, sınıf yöntemlerinin zinciri ve tek nesneye eklenen tekil yöntem.

Önceki ders sonunda `Kasa` sınıfında ve karıştırılmış `Kilit` modülünde aynı adlı `ac` yönteminin olduğunu, ve `Kasa.new.ac` çağrısında hangisinin çalıştığını sormayı bıraktı. Önceki üç ders bu sorunun parçalarını ayrı ayrı yanıtladı: sınıf kendi tanımını yazar, üst sınıf devralır, modül karıştırılır. Hepsi aynı arama sırasının içinde.

Bu dersin yazanı ata yazanıdır, ama bu kez atanın bir üst sınıf mı yoksa bir modül mü olduğu önemli değildir. M13/K01'de yazanı tanımın yazarı üzerinden ölçmüştük; burada yazar sorusu aynı kalır, arama sırasının kendisi ölçülür. Tanımın üst sınıfta mı modülde mi durduğu ayrılmaz: ikisi tek bir kategoridir, ata.

## Önüne Eklenen Modül

`include` modülü sınıfın üstüne yerleştirir, yani sınıf kazanır. `prepend` ise modülü sınıfın önüne yerleştirir; alıcıya ulaşan ileti önce modüle gider. Aşağıdaki dosya bir `Kilit` modülünü `Kasa`'ya `prepend` ile ekler.

```ruby
# yazan.rb — bir yontemi kimin yazdigini siniflandiran yardimci

# Siniflandirma sirayla sorar: once alicinin zincirinde bir tanim var mi,
# sonra tanimi alicinin kendi sinifi mi tasiyor, sonra tanim nasil uretildi.
def yazan(alici, ad)
  zincir = alici.singleton_class
  unless zincir.method_defined?(ad) || zincir.private_method_defined?(ad)
    return ["kimse", "tanim yok"]
  end

  tanim = zincir.instance_method(ad)
  sahip = tanim.owner
  yer = sahip == zincir ? "tekil sinif" : sahip.to_s
  return ["ata", yer] unless sahip == alici.class || sahip == zincir

  iz = RubyVM::InstructionSequence.of(tanim)
  return ["kod", "#{yer}, ruby govdesi yok"] if iz.nil?
  return ["kod", "#{yer}, blok govdesi"] if iz.label.start_with?("block")

  ["programci", "#{yer}, def govdesi"]
end
```

```ruby
# one_eklenen.rb — prepend ile modul sinifin onune gecer
require_relative "yazan"

module Kilit
  def ac = "kilit acildi, #{super}"
end

class Kasa
  prepend Kilit

  def ac = "kasa acildi"
end

kasa = Kasa.new
puts kasa.ac
p yazan(kasa, :ac)
p Kasa.ancestors.take(3)
p kasa.method(:ac).owner
p kasa.method(:ac).super_method.owner
```

```
kilit acildi, kasa acildi
["ata", "Kilit"]
[Kilit, Kasa, Object]
Kilit
Kasa
```

`Kasa.new.ac` iki parçayı birleştirdi: önce `Kilit`'in ön eki, ardından `super` ile `Kasa`'nın kendi tanımı. `yazan` ata yazanını ve kanıt olarak `Kilit`'i verdi: zincirde ilk bulunan tanım `Kilit`'te, çünkü `Kasa.ancestors` listesinde `Kilit` `Kasa`'nın önünde durur. `method(:ac).owner` `Kilit`, `super_method.owner` `Kasa` çıktı. Arama soldan sağa yürür, ilk eşleşme çalışır ve `super` o noktadan zincirde bir adım ilerler.

**SM11.** Bu derste "yöntem arama", bir ileti alıcıya ulaştığında Ruby'nin alıcının ata zincirini `ancestors` sırasıyla soldan sağa gezip adı eşleşen ilk tanımı bulmasıdır. "Önüne eklemek", `prepend` ile bir modülü sınıfın önüne yerleştirmektir; `include` ise modülü sınıfın arkasına yerleştirir.

## include ile prepend Yan Yana

İki anahtar sözcüğün farkı, aynı modülle aynı sınıfı iki kez kurup zincirleri karşılaştırınca görünür. Aşağıdaki dosya `Kilit`'i bir sınıfa `include`, öbürüne `prepend` ile ekler; ardından iki modülü art arda önüne ekleyerek sırayı da sınar.

```ruby
# include_prepend.rb — iki anahtar sozcugun zincirdeki yeri
module Kilit
  def ac = "kilit acildi"
end

module Alarm
  def ac = "alarm, #{super}"
end

class KasaI
  include Kilit

  def ac = "kasa acildi"
end

class KasaP
  prepend Kilit

  def ac = "kasa acildi"
end

class KasaIki
  prepend Kilit
  prepend Alarm

  def ac = "kasa acildi"
end

p KasaI.ancestors.take(3)
puts KasaI.new.ac
p KasaP.ancestors.take(3)
puts KasaP.new.ac
p KasaIki.ancestors.take(4)
puts KasaIki.new.ac
```

```
[KasaI, Kilit, Object]
kasa acildi
[Kilit, KasaP, Object]
kilit acildi
[Alarm, Kilit, KasaIki, Object]
alarm, kilit acildi
```

`include` ile `KasaI` zincirinde sınıf önce geldi ve `Kilit` arkasına düştü; `ac` çağrısı sınıfın kendi tanımında durdu, modülün tanımına hiç ulaşmadı. `prepend` ile `KasaP` zincirinde `Kilit` sınıfın önüne geçti, ama `Kilit#ac` `super` çağırmadığı için bu kez sınıfın tanımı çalışmadı. Yani önüne eklenen modül, `super` yazmazsa sınıfı tamamen gölgeler. `KasaIki` zincirinde ikinci `prepend` ilkinin önüne girdi: sonradan eklenen alıcıya en yakın durur, tıpkı `include` sırasında olduğu gibi. Üç sınıfta da kural aynıdır; yalnız halkanın sınıfın önüne mi arkasına mı gireceği değişir.

Zinciri sonuna kadar yürümek için `super_method` bir döngüde de kullanılabilir. Aşağıdaki dosya tekil yöntemi, önüne eklenen modülü ve sınıfı üst üste koyar ve her halkanın sahibini sırayla toplar. Tekil sınıfın adı bellek adresi taşıdığı için sahip, çıktıya adıyla değil `:tekil` etiketiyle yazılır.

```ruby
# zinciri_yuru.rb — super_method ile halkalari saymak
module Kilit
  def ac = "kilit acildi, #{super}"
end

class Kasa
  prepend Kilit

  def ac = "kasa acildi"
end

k = Kasa.new
def k.ac = "tekil, #{super}"

sahipler = []
yontem = k.method(:ac)
while yontem
  sahip = yontem.owner
  sahipler << (sahip == k.singleton_class ? :tekil : sahip)
  yontem = yontem.super_method
end

p sahipler
puts k.ac
p Kasa.new.method(:ac).super_method.super_method
```

```
[:tekil, Kilit, Kasa]
tekil, kilit acildi, kasa acildi
nil
```

Döngü üç halka buldu: tekil sınıf, `Kilit` ve `Kasa`; bu sıra `ancestors` okuması ile birebir örtüşür. `puts k.ac` her halkanın kendi parçasını aynı sırayla birleştirdi. Tekil yöntemi olmayan bir `Kasa` nesnesinde zincir iki halkadır ve `super_method.super_method` `nil` verir: `Kasa`'dan sonra zincirde `ac` tanımlayan başka halka yoktur.

## Zincirin Sonu

Zincir sonsuz değildir. `Object`'ten türeyen her sınıfın zinciri `Object`, `Kernel` ve `BasicObject` ile biter. Aşağıdaki dosya `Hesap` sınıfının zincirini sonuna kadar okur ve son duraktaki özel bir yöntemi sorar.

```ruby
# zincirin_sonu.rb — zincir BasicObject ile biter
class Hesap
end

p Hesap.ancestors
p Hesap.ancestors.last
p Hesap.ancestors.size
p BasicObject.private_method_defined?(:method_missing)
p Kernel.private_method_defined?(:puts)
```

```
[Hesap, Object, Kernel, BasicObject]
BasicObject
4
true
true
```

Boş bir `Hesap` sınıfının zinciri `[Hesap, Object, Kernel, BasicObject]` dizisidir ve `ancestors.size` dördü verdi; karıştırılmış bir modül olsaydı beş olurdu. `BasicObject` zincirin son durağıdır. `method_missing` orada özel olarak tanımlıdır: zincirde ad bulunamazsa son çare olarak bu yöntem çağrılır ve varsayılan davranışı `NoMethodError` yükseltmektir. `puts` ise `Kernel`'de özel olarak durur; bu yüzden her nesnenin içinden alıcısız çağrılabilir, ama `"x".puts` yazılamaz.

Zincirin sonuna ulaşıp ad bulunamazsa ne olacağı `BasicObject`'in özel `method_missing` tanımına bağlıdır. Bir sınıf bu yöntemi kendi gövdesinde yeniden tanımlarsa, eşleşmeyen ileti ona düşer. Aşağıdaki dosya iki sınıfın aynı eşleşmeyen çağrıya verdiği yanıtı karşılaştırır.

```ruby
# son_care.rb — zincirde bulunamayan ad method_missing'e duser
class Hesap
end

class Hesap2
  def method_missing(ad, *) = "yok: #{ad}"
  def respond_to_missing?(ad, ozel = false) = false
end

begin
  Hesap.new.baska
rescue NoMethodError => e
  puts e.class
end

puts Hesap2.new.baska
p Hesap2.new.respond_to?(:baska)
p Hesap2.instance_method(:method_missing).owner
p Hesap.instance_method(:method_missing).owner
```

```
NoMethodError
yok: baska
false
Hesap2
BasicObject
```

`Hesap` için zincir boyunca ad bulunamadı, iletiyi `BasicObject#method_missing` aldı ve `NoMethodError` yükseltti. `Hesap2` kendi `method_missing`'ini yazdığı için ileti zincirin sonuna inmeden, sınıfın kendi tanımına düştü. `respond_to?` yanlış verdi; bir nesnenin böyle yanıt vermesi, onun yöntem tanımlamış olduğu anlamına gelmez. `owner` karşılaştırması `Hesap2` için sınıfın kendisini, `Hesap` için `BasicObject`'i gösterdi. Yani zincirin sonu bir varsayılan davranıştır ve sınıf onu zincirin herhangi bir halkasında değiştirebilir.

**SM16.** Bu derste "zincirin sonu", alıcının zincirinde adın bulunamadığı durumda iletiyi alan `method_missing` çağrısıdır. Varsayılan tanımı `BasicObject`'tedir ve `NoMethodError` yükseltir; zincirde daha yakın bir halka onu yeniden tanımlayabilir.

## Sınıf Yöntemlerinin Zinciri

`Hesap.new` çağrısındaki `new` bir yöntemdir ve alıcısı `Hesap` sınıfının kendisidir. Sınıfın da bir zinciri vardır. Bu zincir, sınıfa özel tekil sınıf üzerinden kurulur.

```ruby
# sinif_zinciri.rb — sinifin kendi zinciri
class Hesap
end

p Hesap.method(:new).owner
p Hesap.singleton_class
p Hesap.singleton_class.ancestors.take(4)
```

```
Class
#<Class:Hesap>
[#<Class:Hesap>, #<Class:Object>, #<Class:BasicObject>, Class]
```

`Hesap.new` için `new` yönteminin sahibi `Class` çıktı: sınıf yöntemleri de aynı arama kuralıyla bulunur, yalnız alıcı bir sınıftır ve zincir onun tekil sınıfından başlar. Sınıfın tekil sınıfı `#<Class:Hesap>` adıyla yazdırılır. Zincir bu tekil sınıftan `Object`'in tekil sınıfına, `BasicObject`'in tekil sınıfına ve en sonunda `Class`'a doğru yürür. Yani üst sınıfta tanımlı bir sınıf yöntemi, alt sınıftan çağrılınca da bulunur.

Zincirin tekil sınıflardan sonra neden `Class`'a bağlandığı ve alt sınıfın üst sınıfın sınıf yöntemini nasıl bulduğu iki ayrı soru. Aşağıdaki dosya ikisini ölçer; karşılaştırmalar çıktıdaki adla değil, eşitlik sorusuyla yapılır, böylece sonuç adın yazılışına değil nesnelerin kendisine dayanır.

```ruby
# sinif_zinciri_ek.rb — sinif yontemi alt siniftan bulunur
class Hesap
  def self.varsayilan = new("-", 0)

  def initialize(sahip, bakiye)
    @sahip = sahip
    @bakiye = bakiye
  end
end

class Vadeli < Hesap
end

p Vadeli.varsayilan.class
p Vadeli.method(:varsayilan).owner == Hesap.singleton_class
p Vadeli.singleton_class.superclass == Hesap.singleton_class
p Vadeli.singleton_methods
p Vadeli.singleton_methods(false)
p BasicObject.singleton_class.superclass
p Hesap.singleton_class.ancestors.drop(3)
```

```
Vadeli
true
true
[:varsayilan]
[]
Class
[Class, Module, Object, Kernel, BasicObject]
```

`Vadeli.varsayilan` bir `Vadeli` nesnesi üretti: yöntem `Hesap`'ın tekil sınıfında yazılı olduğu hâlde alıcı `Vadeli` olduğu için `new` ona uygulandı. `owner` karşılaştırması tanımın `Hesap`'ın tekil sınıfında durduğunu, `superclass` karşılaştırması `Vadeli`'nin tekil sınıfının `Hesap`'ın tekil sınıfına bağlandığını gösterdi. `singleton_methods` üst sınıftan gelen sınıf yöntemini de saydı, `singleton_methods(false)` ise boş kaldı. Zincirin tekil sınıflardan sonra `Class`'a geçmesinin nedeni de buradan okunur: `BasicObject`'in tekil sınıfının üst sınıfı `Class`'tır, o noktadan sonra zincir `Module`, `Object`, `Kernel` ve `BasicObject` ile sıradan sınıf zincirine karışır.

## Sınırlayıcı Ölçüm

Şimdiye dek zincirler sınıflardan ve modüllerden kuruluydu. Bir nesnenin zinciri o nesneye özgü bir halka da taşıyabilir: tekil sınıf. Aşağıdaki dosya tek bir `Kasa` nesnesine bir tekil yöntem ekler ve bir modül `extend` eder.

```ruby
# tekil.rb — tek nesneye ozgu halka
require_relative "yazan"

module Ek
  def ac = "ek, #{super}"
end

module Kilit
  def ac = "kilit acildi, #{super}"
end

class Kasa
  prepend Kilit

  def ac = "kasa acildi"
end

k = Kasa.new
def k.ac = "tekil, #{super}"

puts k.ac
puts Kasa.new.ac
p yazan(k, :ac)
p k.singleton_methods

j = Kasa.new
j.extend(Ek)
p j.singleton_class.ancestors.drop(1).take(4)
puts j.ac
```

```
tekil, kilit acildi, kasa acildi
kilit acildi, kasa acildi
["programci", "tekil sinif, def govdesi"]
[:ac]
[Ek, Kilit, Kasa, Object]
ek, kilit acildi, kasa acildi
```

`def k.ac` tek bir nesneye bir tekil yöntem ekledi. `k.ac` önce kendi ön ekini, sonra zincirin geri kalanını verdi; yeni üretilen bir `Kasa` ise etkilenmedi. Yazan programcı çıktı, kanıt olarak `tekil sinif, def govdesi`: kuralın dilinde tanımın sahibi nesnenin tekil sınıfıdır. `k.singleton_methods` yalnız `[:ac]` verdi. `j.extend(Ek)` ise `Ek` modülünü `j` nesnesinin tekil sınıfına karıştırdı ve `j.singleton_class.ancestors.drop(1)` listesi `Ek`'le başlayıp ardından `Kilit`, `Kasa`, `Object` ile devam etti. Zincirin başındaki tekil sınıf adı çıktıya yazılmadı, çünkü o ad bellek adresi taşır.

**SM12.** Bu derste "tekil sınıf", tek bir nesneye ya da bir sınıfa özgü, ata zincirinin en başına eklenen gizli sınıftır. "Tekil yöntem", o sınıfta tanımlanan yöntemdir. Başka nesneler bu zinciri paylaşmaz.

## Yöntemi Kimin Verdiğini Sormak

Zincir okunurken sık yapılan bir ayrım, `method` ile `instance_method` arasındadır. İlki nesneye, ikincisi sınıfa sorar; bu yüzden tekil yöntemi yalnız ilki görür. Aşağıdaki dosya ikisini ve `Kernel`'den gelen bir yöntemi yan yana koyar.

```ruby
# kim_verdi.rb — method ile instance_method farki
module Kilit
  def ac = "kilit acildi"
end

class Kasa
  prepend Kilit

  def ac = "kasa acildi"
end

k = Kasa.new
def k.ac = "tekil acildi"

p k.method(:ac).owner == k.singleton_class
p Kasa.instance_method(:ac).owner
p Kasa.new.method(:ac).owner
p k.method(:puts).owner
p k.method(:class).owner
p Kasa.instance_method(:ac).super_method.owner
```

```
true
Kilit
Kilit
Kernel
Kernel
Kasa
```

`method` nesneye sorduğu için ilk satırda tekil sınıfın sahip çıktığı görüldü. `instance_method` yalnız sınıfın zincirine baktı; tekil yöntem o zincirde olmadığından cevap `Kilit` oldu, yani önüne eklenen modül sınıfın önünde durduğu için sahip olarak o göründü. `Kasa.new` ile tekil yöntemi olmayan bir nesnede de `method` aynı cevabı verdi. `puts` ve `class` için sahip `Kernel` çıktı: bu iki yöntemi yazan ne programcıdır ne sınıf, `Object` zincirine karışan `Kernel` modülüdür. Son satır, `Kilit#ac`'ın bir altındaki halkayı sorar ve `Kasa#ac`'ı bulur; tek halkalık bir önüne ekleme bile `super_method` ile iki halkalık bir zincir olarak okunabilir.

Bu soruların hepsi aynı alışkanlığa çıkar: bir yöntemin hangi tanımla çalışacağı, tanımın metne göre nerede yazıldığıyla değil, alıcının zincirinde hangi halkanın önce geldiğiyle belirlenir. Sahip sorusu, o halkanın adını verir.

## Aramayı Okumanın Üç Kuralı

Yukarıdaki ölçümlerden arama için üç kural çıkar. Birincisi, sıra alıcının zincirinden gelir: yöntemin hangi sınıfta yazıldığına değil, alıcının hangi zincire sahip olduğuna bakılır. İkincisi, zincirdeki ilk eşleşmeyi alır, öbür tanımlar yalnız `super` ile yeniden çağrılırsa çalışır. Üçüncüsü, `super` zincirin başına dönmez, bulunduğu halkadan bir sağa ilerler. `Kilit#ac` içindeki `super` bu yüzden `Kilit`'e değil `Kasa`'ya iner.

Bu kurallar `include`, `prepend` ve tekil yöntemin tek bir mekanizmanın üç görünümü olduğunu da söyler. Üçü de zincire bir halka ekler; fark halkanın nereye gireceğidir. `include` halkayı sınıfın arkasına koyar, `prepend` önüne, tekil sınıf ise nesnenin kendi zincirinin en başına. Hangi halkanın kazandığını anlamak için tahmin yürütmek gerekmez: `ancestors` dizisini soldan sağa okumak yeter. Tekil sınıf listede görünmez, ama `singleton_class.ancestors` ile sorulursa zincirin başında durur.

Zincirin okunabilir olması, hata ayıklamada da işe yarar. Bir yöntem beklenmeyen bir sonuç verdiğinde ilk soru hangi tanımın çalıştığıdır. Bunun yanıtı `method(:ad).owner` satırıdır. İkinci soru o tanımın arkasında başka bir tanım olup olmadığıdır; yanıt `super_method` ile bir halka daha sorularak alınır ve `nil` dönene kadar tekrarlanır. `super_method` zincirdeki sıradaki tanımı verir; tanımın `super` çağırıp çağırmadığını yöntemin gövdesi gösterir. Bu iki yöntemle zincir tahmin edilmez, okunur.

## Özet

- Yöntem arama, alıcının `ancestors` zincirinde soldan sağa yürür ve adı eşleşen ilk tanımı çalıştırır.
- `prepend` modülü sınıfın önüne koyar; `Kasa.new.ac` önce `Kilit`'i, `super` ile sonra `Kasa`'yı çalıştırır.
- `method(:ac).owner` ilk tanımın sahibini, `super_method.owner` zincirde bir sonrakini verir.
- `Object`'ten türeyen bir sınıfın zinciri `BasicObject` ile biter; `method_missing` orada, `puts` ise `Kernel`'de özel olarak durur.
- Sınıf yöntemleri sınıfın tekil sınıf zincirinden gelir: `Hesap.new` için `new` yönteminin sahibi `Class`'tır.
- Bu dersin yazanı atadır; tek nesneye eklenen tekil yöntem ise zincirin başına girer ve yazanı programcı yapar.

## Sonraki Adım

`Nokta` adlı bir `Struct` sınıfında `x` yöntemi hiç `def` ile yazılmadı, yine de `nokta.x` çalışıyor ve `yazan` onun yazanını kod olarak veriyor. Yöntemleri bir nesneden çalışma zamanında sormak ve `x`'i kimin yazdığını nesneye sordurmak mümkün mü? Sonraki ders, Yansıma, nesneye bu soruları sormayı ölçer.
