---
title: 'Dinamik Yöntem Tanımı'
source: 'https://academia.sh/tr/kurslar/ruby-nesne-yonelimi/dinamik-yontem-tanimi'
course: 'Nesne Yönelimi ve Metaprogramlama'
language: tr
updated: '2026-10-21T05:03:42+00:00'
license: 'CC BY-SA 4.0'
objectives:
    - 'define_method ile bir bloktan yöntem tanımlar'
    - 'Üretilen yöntemin def ile yazılandan hangi sorularda ayrıldığını iz etiketiyle ölçer'
    - 'method_added kancasının def, define_method ve attr_reader için aynı biçimde tetiklendiğini gösterir'
    - 'class_eval ile dizgiden üretilen yöntemin yazanını sınıflandırır'
---

# Dinamik Yöntem Tanımı

define_method ile çalışma zamanında yöntem üretmenin, bu yöntemlerin parametre denetiminin ve üretimin yazanı nasıl sınıflandırdığının ölçümü.

Önceki ders yansımayı verdi: bir nesneye yöntemlerini, durumunu ve türünü sormak. `Nokta#x` yöntemini `Struct.new` yazmıştı ve `yazan` yardımcısı ona **kod** demişti. Orada üretim bir kara kutuydu; yalnız sonuca baktık. Bu ders üretimin kendisini yapar. **Dinamik yöntem tanımı (dynamic method definition)**, bir yöntemin `def` yazılarak değil, program çalışırken ad ve gövde verilerek tanımlanmasıdır. Ruby'de bunun aracı `define_method`'dur. M11/K01, C'de bir kararın hangi aşamada verildiğini ölçmüştü; burada tanımın ne zaman oluştuğu sorulmuyor, soru yine kimin yazdığıdır.

Eksen aynı: **bu yöntemi kim yazdı?** Bu dersin yazanı **kod**. Programcı bir kez döngü yazar, döngü iki yöntemi yazar. Programcının elinden çıkan yalnız `define_method` çağrısıdır, tek tek yöntem gövdeleri değil. Dört yazanı hatırlayalım: programcı `def` ile yazar, ata bir üst sınıf ya da modüldür, kod bir üretim çağrısıdır, kimse ise o ada tanım olmamasıdır. Dinamik tanım, kodun yollarından biridir. Neden gerektiği de açıktır: aynı kalıpta onlarca yöntem gerekiyorsa, onları tek tek yazmak yerine kalıbı bir kez yazıp adları bir listeden almak hem kısadır hem de listeye bir ad eklendiğinde yöntemin kendiliğinden doğmasını sağlar. Bu derste o kısalığın bedelini ölçeceğiz: kaynakta aranan bir yöntem adı, kaynakta hiç geçmeyebilir. Aşağıdaki yardımcı önceki dersle aynıdır ve olduğu gibi verilir.

```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
```

Bu blok kaynaktır; dosya diske yazılır ve `require_relative` ile çekilir.

## Ölçülen Sınıflar

Bu dersin gözlemi iki sınıfa dayanır. `Hesap`, `def` ile yazılmış bir yöntemi karşılaştırma için sağlar. `Ayar` ise yöntemlerini bir döngüden alır. Ortak tanımların kesiti şudur.

```ruby
# yazanlar.rb — bu dersin gozlemi icin gereken tanimlar
require_relative "yazan"

module Raporlanir
  def rapor = "rapor: #{bakiye_metni}"
end

class Hesap
  include Raporlanir

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

  def bakiye_metni = "#{@sahip} #{@bakiye}"
  def net = @bakiye - komisyon

  private

  def komisyon = 2
end

class Ayar
  %i[renk boyut].each do |ad|
    define_method(ad) { "#{ad} varsayilan" }
  end
end
```

Bu blok de kaynaktır. `Ayar` tanımında `%i[renk boyut]` iki `Symbol` değerinden oluşan bir dizi kurar; `each` her ad için `define_method` çağırır ve bloğu o adın yöntemi yapar. `Ayar` sınıfında `renk` ve `boyut` adında bir `def` yoktur.

## Üretilen Yöntemi Kim Yazdı

Bu dersin gözlemi `Ayar` nesnesinin `renk` yöntemidir. Aşağıdaki dosya yardımcıyı çağırır ve yöntemin ürettiği değeri yanına basar.

```ruby
# uretilen.rb — Ayar#renk yontemini kim yazdi
require_relative "yazanlar"

a = Ayar.new
p yazan(a, :renk)
puts a.renk
```
```
["kod", "Ayar, blok govdesi"]
renk varsayilan
```

Yazan **kod**. Yöntemin sahibi `Ayar`'ın kendisi çıktı, yani ata değil; ama gövde bir `def` değil, `define_method`'a verilen bir blok. Yardımcı bu ayrımı iz etiketinin `block` ile başlamasından okudu. Önceki dersteki `Nokta#x` da koddu, ama onun gövdesi hiç yoktu: orada cevap `ruby govdesi yok` idi, burada `blok govdesi`. Aynı kategoriye iki ayrı biçim düşer. **MP10.** Yalnız `renk` yöntemi ölçüldü; `boyut` aynı döngüden geldiği için aynı sonucu verecektir, ama bu bir varsayımdır.

## Üretim İzi

Yöntem nesnesinin gövdesinin bir **iz etiketi (label)** vardır; Ruby bunu derlenmiş gövdeye yazar. Yardımcı etikete bakıp `block` ile başlayıp başlamadığını sorar. Etiketi doğrudan basarak iki yöntemi karşılaştırırız: döngüyle üretilen `renk` ve `def` ile yazılan `net`.

```ruby
# iz.rb — iki yontemin gövde etiketi
require_relative "yazanlar"

p Ayar.instance_methods(false)
iz = RubyVM::InstructionSequence
puts iz.of(Ayar.instance_method(:renk)).label
puts iz.of(Hesap.instance_method(:net)).label
```
```
[:boyut, :renk]
block (2 levels) in <class:Ayar>
net
```

`instance_methods(false)` yalnız `Ayar`'ın kendi herkese açık yöntemlerini listeledi. Etiketler farkı gösterir: `renk` için `block (2 levels) in <class:Ayar>`, `net` için `net`. İki seviye, tanımın iki blok içinde olduğunu söyler: biri `each` bloğu, öteki `define_method` bloğu. `net`'in etiketi ise yöntemin kendi adıdır. Yardımcının `start_with?("block")` sorusu bu iki etiketi ayırır. **MP11.** Etiket biçimi Ruby 4.0.7'de ölçüldü; başka bir sürümde metin değişebilir, ama `block` ile başlama kuralı bu ölçümden çıkarılan tek şeydir.

## Çalışırken Yöntem Eklemek

`define_method` bir ad ve bir blok alır, yöntemi o ada bağlar ve adı `Symbol` olarak döndürür. Yöntem aynı satırdan sonra çağrılabilir. Sınıf zaten yüklenmişken de çalışır: sınıfa yeni bir yöntem eklemek için `class` anahtar sözcüğüyle yeniden açmak gerekmez.

```ruby
# tanim.rb — define_method ile yontem eklemek ve kaldirmak
require_relative "yazanlar"

donus = Ayar.define_method(:tema) { "koyu" }
p donus
p Ayar.new.tema
p yazan(Ayar.new, :tema)
Ayar.remove_method(:tema)
begin
  Ayar.new.tema
rescue NoMethodError => e
  puts e.class
end
```
```
:tema
"koyu"
["kod", "Ayar, blok govdesi"]
NoMethodError
```

Dönüş `:tema` oldu; yeni yöntem hemen cevap verdi ve `yazan` onu **kod** olarak sınıflandırdı, tıpkı döngüyle üretilenler gibi. Üretimin zamanı değişti, tanımın biçimi değişmedi: gövde yine bir blok. `remove_method` yöntemi `Ayar`'ın kendi tablosundan sildi ve ardından aynı ileti `NoMethodError` verdi. Silinen yöntem, aynı adda bir ata tanımı olsaydı, o ata tanımı yeniden görünür olurdu; `remove_method` yalnız sınıfın kendi tanımını siler. **MP12.** `remove_method` yalnız bu sınıfta ve ata tanımı olmayan bir adla ölçüldü; ata tanımı olan durum burada gösterilmedi.

## Adı Çalışırken Belirlemek

`def` ile yazılan yöntemin adı kaynakta sabittir. `define_method` ise adı bir dizgi ya da `Symbol` olarak alır; ad bir değişkenden ya da hesaplamadan gelebilir. Aşağıdaki sınıf iki alan adı için birer okuyucu ve yazıcı üretir; ürettiği yöntem adlarını kaynakta hiçbir yerde düz yazı olarak taşımaz.

```ruby
# form.rb — adi hesaplanan yontemler
require_relative "yazanlar"

class Form
  %w[ad soyad].each do |alan|
    define_method("#{alan}_yaz") { |deger| instance_variable_set("@#{alan}", deger) }
    define_method("#{alan}_oku") { instance_variable_get("@#{alan}") }
  end
end

f = Form.new
f.ad_yaz("ece")
p f.ad_oku
p f.soyad_oku
p f.instance_variables
p Form.instance_methods(false).sort
p yazan(f, :ad_yaz)
```
```
"ece"
nil
[:@ad]
[:ad_oku, :ad_yaz, :soyad_oku, :soyad_yaz]
["kod", "Form, blok govdesi"]
```

Dört yöntem tek bir döngüden çıktı: `ad_oku`, `ad_yaz`, `soyad_oku` ve `soyad_yaz`. Yazılmış alan okunduğunda `"ece"` geldi; yazılmamış alan `nil` verdi, çünkü o örnek değişkeni hiç atanmadı. `instance_variables` yalnız `@ad`'ı listeledi: ikinci alanın örnek değişkeni, ona ilk değer yazılana kadar var değildir. Yazan yine **kod** çıktı. Burada programcı yalnız bir kalıp yazdı; kalıbın dört örneğini kod üretti. **MP13.** Bu sınıf iki alanla ölçüldü; ad dizgisinin yöntem adı olarak geçersiz karakterler içermesi durumu sınanmadı.

## Parametre Denetimi

Blok normalde gevşek davranır: eksik argüman `nil` olur. `define_method`'a verilen blok ise yöntem gibi davranır ve argüman sayısını denetler. Aşağıdaki ölçüm bunu iki yoldan sınar: bloğu doğrudan vermek ve bir `proc` nesnesini `&` ile vermek.

```ruby
# parametre.rb — define_method bloklari ve argüman denetimi
class Ayar
  define_method(:carp) { |n| n * 2 }
  define_method(:pcarp, &proc { |n| n * 2 })
end

a = Ayar.new
p Ayar.instance_method(:carp).arity
p a.carp(4)
p proc { |n| n }.call
[:carp, :pcarp].each do |ad|
  begin
    a.public_send(ad)
  rescue ArgumentError => e
    puts "#{ad} -> #{e.class}"
  end
end
```
```
1
8
nil
carp -> ArgumentError
pcarp -> ArgumentError
```

`carp` için argüman sayısı `1` çıktı: üretilen yöntem, bloğun parametresini zorunlu bir parametre olarak taşıyor. `carp(4)` `8` verdi. Üçüncü satır karşıtı gösterir: argümansız çağrılan sıradan bir `proc` hata vermedi, `nil` döndürdü. Aynı biçimdeki blok `define_method` içinde ise `ArgumentError` verdi; `proc` nesnesini `&` ile geçirmek bunu değiştirmedi, `pcarp` da hata verdi. Yani üretim anında blok yöntem kuralına geçer. **MP14.** Yalnız tek zorunlu parametreli bloklar ölçüldü; varsayılan değerli ve değişken argümanlı bloklar bu derste sınanmadı.

## Bloğun Gördüğü Değişkenler

`define_method` ile `def` arasındaki en kullanışlı fark, bloğun çevresindeki yerel değişkenleri görmesidir. **Kapanış (closure)**, bir bloğun tanımlandığı yerdeki yerel değişkenleri sonradan çağrıldığında da taşımasıdır. `def` yeni bir kapsam açar ve çevresindeki yerel değişkenleri görmez; blok ise görür.

```ruby
# sayac.rb — define_method blogu cevresindeki degiskeni gorur
class Sayac
  adet = 0
  define_method(:say) { adet += 1 }
  def bak = defined?(adet).inspect
end

a = Sayac.new
b = Sayac.new
p [a.say, a.say, b.say]
puts a.bak
```
```
[1, 2, 3]
nil
```

Üç çağrı `[1, 2, 3]` verdi. `adet` sınıf gövdesinde bir yerel değişkendi ve `say` bloğu onu kapanış olarak taşıdı. Sayaç iki nesne arasında paylaşıldı: ikinci nesnenin ilk çağrısı `3` oldu, çünkü değişken nesnede değil, bloğun kapanışında duruyor. `def` ile yazılan `bak` ise aynı adı göremedi; `defined?(adet)` orada `nil` döndü ve `inspect` bunu `"nil"` olarak yazdırdı. Bu yüzden `define_method` ile üretilen bir yöntemin durumu örnek değişkenlerinde olmayabilir; yansıma ile `instance_variables` çağrısı bu sayacı göstermez. **MP15.** Bloğun kapanışı yalnız bu sınıf gövdesindeki tek yerel değişkenle ölçüldü; kapanışın başka yöntemler arasında paylaşılması bu örnekte gösterilmedi.

## Kanca

Bir sınıfa yeni bir yöntem eklendiğinde Ruby sınıfın `method_added` yöntemini çağırır. **Kanca (hook)**, belirli bir olay olduğunda çalışması için tanımlanan yöntemdir. Aşağıdaki sınıf bu kancayı yazar ve eklenen adları bir listede toplar. Üç ayrı yolla yöntem eklenir: `def`, `define_method` ve `attr_reader`.

```ruby
# kanca.rb — method_added hangi yollarda tetiklenir
class Kumbara
  @eklenen = []

  class << self
    attr_reader :eklenen

    def method_added(ad)
      @eklenen << ad
    end
  end

  def a = 1
  define_method(:b) { 2 }
  attr_reader :c
end

p Kumbara.eklenen
```
```
[:a, :b, :c]
```

Üç yol da kancayı aynı biçimde tetikledi: `[:a, :b, :c]`. Kanca yöntemi hangi yolla üretildiğini bilmez, yalnız adı alır. Bu, önceki derslerin gözlemiyle birlikte okunmalıdır: kanca tetiklemesi yazanı söylemez. Yazanı söyleyen şey, tanımın gövdesinin biçimidir; kanca bunu görmez. Bu yüzden kanca yöntem eklenen yeri izlemek için iyidir, ama kim yazdı sorusuna cevap vermez. **MP16.** Kanca yalnız bu sınıfta, sınıf gövdesi çalışırken ölçüldü.

## Erişimci de Kod Üretir

`attr_accessor`, aynı adla bir okuyucu ve bir yazıcı yöntem üretir. **Erişimci (accessor)** bu iki yöntemin birlikte adıdır; yalnız okuyucuya `attr_reader`, yalnız yazıcıya `attr_writer` denir.

```ruby
# erisim.rb — attr_accessor iki yontem uretir
require_relative "yazan"

class Erisim
  attr_accessor :renk
end

p Erisim.instance_methods(false).sort
p yazan(Erisim.new, :renk)
p yazan(Erisim.new, :renk=)
```
```
[:renk, :renk=]
["kod", "Erisim, ruby govdesi yok"]
["kod", "Erisim, ruby govdesi yok"]
```

İki yöntem çıktı: `renk` ve `renk=`. İkisinin de yazanı **kod**, ama gövde biçimi `define_method`'dan farklı: etiket yok, çünkü `attr_accessor` Ruby gövdesi bırakmaz. `Erisim` sınıfında hiçbir `def` yazılmadı. `blok govdesi` ile `ruby govdesi yok` kodun iki alt biçimidir. **MP17.** Yalnız `attr_accessor` ölçüldü; `attr_writer` tek başına ölçülmedi.

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

Şimdiye kadar kod, `define_method` ya da `attr_*` çağrısı demekti. Bir üretim biçimi daha var: sınıfa bir dizgiyi Ruby kodu olarak yorumlatmak. `class_eval` bir dizgiyi sınıfın bağlamında çalıştırır. Aşağıdaki ölçüm aynı işi iki yolla yapar: biri dizgiyle, öteki blokla.

```ruby
# sinir.rb — ayni is, iki uretim yolu
require_relative "yazanlar"

Ayar.class_eval "def dizgiden = 4"
Ayar.define_method(:blokla) { 4 }

a = Ayar.new
p [a.dizgiden, a.blokla]
p yazan(a, :dizgiden)
p yazan(a, :blokla)
```
```
[4, 4]
["programci", "Ayar, def govdesi"]
["kod", "Ayar, blok govdesi"]
```

İki yöntem de `4` verdi, ama yazanları ayrıldı. Dizgiden üretilen yöntem çalışma zamanında yaratıldığı hâlde `yazan` onu **programcı** olarak sınıflandırdı, çünkü dizgi içinde bir `def` gövdesi var. `define_method` ile üretilen ise **kod**. Kural biçimi okur, niyeti okumaz: kategori üretimin ne zaman olduğunu değil, tanımın hangi biçimde olduğunu ölçer. Bu yüzden kod üretimi her zaman koda düşmez. Tersi de doğrudur: kaynakta `define_method` görmek, yazanın kod olduğunu söylemez; bir dizginin içinde yazılmış `def` ise çalışma zamanında üretilmiş olsa bile programcıya düşer. Yansıma yöntemin sahibini ve gövdesinin biçimini görür, üretimi başlatan çağrıyı görmez. Bu yüzden üretimin izini sürmek, etikete bakmaktan çok kaynakta `define_method` ve `class_eval` çağrılarını aramaktır. **MP18.** Dizgiden üretim yalnız basit tek satırlık `def` ile ölçüldü; daha karmaşık dizgilerde etiket ve gövde biçimi farklı olabilir.

## Özet

- `define_method` bir ad ve bir blok alır, yöntemi o ada bağlar ve adı `Symbol` olarak döndürür; yeni yöntem hemen çağrılabilir.
- Üretilen yöntemin yazanı koddur: gövdesi bir bloktur ve iz etiketi `block` ile başlar. `def` ile yazılanın etiketi yöntemin adıdır.
- `define_method` bloğu argüman sayısını yöntem gibi denetler; sıradan `proc` denetlemez.
- `method_added` kancası `def`, `define_method` ve `attr_reader` için aynı tetiklenir; kanca yazanı söylemez.
- `attr_accessor` iki yöntem üretir ve ikisinin de yazanı koddur, ama gövde biçimi farklıdır.
- Dizgiden `class_eval` ile üretilen `def` yazanı programcı çıkar: kategori tanımın biçimini ölçer, üretim anını değil.

## Sonraki Adım

Bu derste yöntemleri biz tanımladık, çağrılmadan önce. Sıradaki ders tersini ölçer: çağrılan ada hiçbir tanım yoksa Ruby ne yapar ve bu boşluğu bir sınıf kendisi nasıl doldurabilir. O derste yazanın dördüncü kategorisi, kimse, ortaya çıkar.
