---
title: 'Sınıf Tanımı ve Örnek Değişkenler'
source: 'https://academia.sh/tr/kurslar/ruby-nesne-yonelimi/sinif-tanimi-ve-ornek-degiskenler'
course: 'Nesne Yönelimi ve Metaprogramlama'
language: tr
updated: '2026-10-21T05:03:41+00:00'
license: 'CC BY-SA 4.0'
objectives:
    - 'Bir yöntemin yazanını programcı, ata, kod ve kimse olarak sınıflandırır'
    - 'Örnek değişkenlerinin nesnede, yöntemlerin sınıfta durduğunu instance_variables ve instance_methods ile ölçer'
    - 'Aynı sınıfta def ile yazılmış yöntem ile attr_reader ile üretilmiş yöntemi iseq üzerinden ayırır'
---

# Sınıf Tanımı ve Örnek Değişkenler

Bir yöntemi kimin yazdığını dört yazanla sınıflandırma; örnek değişkenlerinin nesnede, yöntemlerin sınıfta durması; initialize'ın özel oluşu; attr_reader ile yazılmış yöntemin def ile yazılmışa göre farkı.

Önceki kurs bir iletinin karşılığını nerede bulduğunu ölçtü: alıcının sınıfında, karıştırılmış bir modülde, verilen blokta ya da sözdiziminde (M13/K01). Kapanışında bir soru açık kaldı. `hesap.bakiye_metni`, `nokta.x`, `ayar.renk` ve `vekil.sort` aynı biçimde çağrılır, ama dördünün gövdesi dört ayrı yoldan gelir. Yöntemin belirli bir sahipte durduğu biliniyor; o tanımı kimin yazdığı bilinmiyor.

Bu kurs o soruyu ölçer. İlk ders bir sınıfın en küçük hâlini alır: örnek değişkenleri nerede durur, yöntemler nerede durur, ve bir yöntemin yazanı nasıl sınıflandırılır?

## Dört Yazan ve Ölçüm Çekirdeği

Kursun her dersi aynı soruyu sorar: bu yöntemi kim yazdı? Yanıt dört kategoriden biridir. **Programcı**, gövdeyi alıcının kendi sınıfında `def` ile yazan kişidir. **Ata**, tanımı zincirde bir üst sınıfta ya da bir modülde bırakandır. **Kod**, tanımı çalışma zamanında bir çağrıyla üreten düzenektir: `attr_reader`, `define_method`, `Struct`. **Kimse**, zincirde hiç tanım bulunmayan ve iletinin `method_missing` ile yakalandığı durumdur. Çıktıda bu dört ad ASCII yazılır: `programci`, `ata`, `kod`, `kimse`.

**SM1.** Bu kursta "yazan", bir yöntemin tanımının nereden geldiğini söyleyen kategoridir. Sınıflandırmayı etiket değil kural yapar: alıcının tekil sınıfında tanım yoksa kimse; tanımın sahibi alıcının sınıfı ya da tekil sınıfı değilse ata; tanımın `iseq` değeri `nil` ya da etiketi `block` ile başlıyorsa kod; hiçbiri değilse programcı. Kural tanımın biçimini okur, yazanın niyetini okumaz. Bu cümle kursun birkaç sınırlayıcı ölçümünün ortak yüzüdür.

Kural `yazan.rb` dosyasındadır. Kurs boyunca yeniden yazılmaz, her derste aynı biçimde yüklenir. Dosyadaki sıra önemlidir. Önce tanımın var olup olmadığı sorulur, çünkü tanımı olmayan bir yöntemin sahibi sorulamaz. Sonra sahip sorulur, çünkü sahibi alıcının sınıfı olmayan bir tanım zaten başka bir yerden gelmiştir. En son tanımın nasıl üretildiğine bakılır: yalnız alıcının kendi sınıfında duran tanımlar için gövdenin bir `def` mi yoksa bir çağrının ürünü mü olduğunu ayırmak gerekir.

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

`yazan(alici, ad)` iki değerli bir dizi döndürür: yazanın adı ve kanıt. Kanıt, tanımın sahibini ve gövdenin nasıl yazıldığını söyler. Dokuz gözlemi bir arada koşan ikinci dosya `yazanlar.rb`'dir. Her gözlem aynı biçimde çağrılan bir yöntemdir; ayrım yalnız çağrının arkasındadır.

```ruby
# yazanlar.rb — dokuz gozlem: bu yontemi kim yazdi
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 Vadeli < Hesap
end

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

class Kasa
  prepend Kilit
  def ac = "kasa acildi"
end

Nokta = Struct.new(:x, :y)

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

class Vekil
  def initialize(hedef)
    @hedef = hedef
  end

  def method_missing(ad, *argumanlar, &blok)
    return super unless @hedef.respond_to?(ad)

    @hedef.public_send(ad, *argumanlar, &blok)
  end

  def respond_to_missing?(ad, ozel = false) = @hedef.respond_to?(ad, ozel) || super
end

class Model
  def self.alan(ad, varsayilan)
    define_method(ad) { instance_variable_get("@#{ad}") || varsayilan }
  end
end

class Urun < Model
  alan :ad, "adsiz"
  alan :fiyat, 0
end

hesap = Hesap.new("ece", 100)
gozlemler = {
  "durum okuma"   => [hesap, :bakiye_metni, -> { hesap.bakiye_metni }],
  "ust sinif"     => [Vadeli.new("can", 50), :bakiye_metni, -> { Vadeli.new("can", 50).bakiye_metni }],
  "karisim"       => [hesap, :rapor, -> { hesap.rapor }],
  "ozel yardimci" => [hesap, :komisyon, -> { hesap.net }],
  "one eklenen"   => [Kasa.new, :ac, -> { Kasa.new.ac }],
  "uye okuyucu"   => [Nokta.new(3, 4), :x, -> { Nokta.new(3, 4).x }],
  "uretilen"      => [Ayar.new, :renk, -> { Ayar.new.renk }],
  "vekil"         => [Vekil.new([3, 1, 2]), :sort, -> { Vekil.new([3, 1, 2]).sort }],
  "bildirim"      => [Urun.new, :fiyat, -> { Urun.new.fiyat }],
}

adet = { "programci" => 0, "ata" => 0, "kod" => 0, "kimse" => 0 }

printf("%-15s%-11s%-27s%s\n", "gozlem", "yazan", "kanit", "sonuc")
gozlemler.each do |ad, (alici, ileti, cagri)|
  tur, kanit = yazan(alici, ileti)
  adet[tur] += 1
  printf("%-15s%-11s%-27s%s\n", ad, tur, kanit, cagri.call.inspect)
end

puts
adet.each { |tur, n| printf("%-11s%d\n", tur, n) }
puts "toplam gozlem: #{adet.values.sum}"
```

```
gozlem         yazan      kanit                      sonuc
durum okuma    programci  Hesap, def govdesi         "ece 100"
ust sinif      ata        Hesap                      "can 50"
karisim        ata        Raporlanir                 "rapor: ece 100"
ozel yardimci  programci  Hesap, def govdesi         98
one eklenen    ata        Kilit                      "kilit acildi, kasa acildi"
uye okuyucu    kod        Nokta, ruby govdesi yok    3
uretilen       kod        Ayar, blok govdesi         "renk varsayilan"
vekil          kimse      tanim yok                  [1, 2, 3]
bildirim       kod        Urun, blok govdesi         0

programci  2
ata        3
kod        3
kimse      1
toplam gozlem: 9
```

Dokuz gözlemin 2'sini programcı, 3'ünü bir ata yazdı; 3'ünü bir kod üretti; 1'inde tanım yoktu, iletiyi `method_missing` yakaladı. Bu sayılar kurs boyunca değişmez. Programcı `durum okuma` ve `ozel yardimci` satırlarını yazdı: ikisinin gövdesi `Hesap` içinde bir `def` ile durur. Ata `ust sinif`, `karisim` ve `one eklenen` satırlarını yazdı; üçünün tanımı alıcıdan uzakta, `Hesap`'ta, `Raporlanir`'de ve `Kilit`'te durur. `uye okuyucu`, `uretilen` ve `bildirim` satırlarında tanımı bir çağrı üretti. `vekil` satırında tanım yok ve sonuç yine de döndü.

Bu dersin payı ilk programcı satırıdır: `durum okuma`. Sonraki dersler ata, kod ve kimse yazanlarını sırayla açar; bu ders ise programcının yazdığı en sade yapıyı, bir sınıfı ve onun örnek değişkenlerini ölçer.

## Durum Nesnede, Davranış Sınıfta

`Hesap` sınıfının `initialize` yöntemi iki değer alır ve ikisini örnek değişkenine atar. Örnek değişkeni `@` ile başlayan, nesneye bağlı bir değişkendir (M13/K01). Aşağıdaki dosya sınıfın küçük bir kesitini yeniden tanımlar, çünkü `yazanlar.rb` yüklendiğinde dokuz satırlık tabloyu basar; bu kesit onu tekrarlamadan çalışır.

```ruby
# durum.rb — ornek degiskenleri nesnede durur
class Hesap
  def initialize(sahip, bakiye)
    @sahip = sahip
    @bakiye = bakiye
  end
end

hesap = Hesap.new("ece", 100)
p hesap.instance_variables
p hesap.instance_variable_defined?(:@bakiye)
p Hesap.instance_methods(false)
begin
  hesap.bakiye
rescue NoMethodError => e
  puts e.class
end
```

```
[:@sahip, :@bakiye]
true
[]
NoMethodError
```

`instance_variables` iki değişkeni atama sırasıyla listeledi: `@sahip` ve `@bakiye`. Değişkenler `Hesap` sınıfında değil, `hesap` nesnesinde durur. `Hesap.instance_methods(false)` ise boş bir dizi verdi: sınıfın genel bir yöntemi yok. `initialize` özeldir ve `false` argümanı yalnız sınıfın kendi tanımlarını sorar. Bu yüzden `hesap.bakiye` çağrısı `NoMethodError` verdi: değişken nesnenin içindedir, ama onu okuyan bir yöntem yazılmadığı için dışarıdan görünmez.

**SM2.** Bu derste "örnek değişkeni", bir nesneye bağlı olan ve `@` ile başlayan değişkendir. Değişkenin bir nesnede bulunması, o değişkeni okuyan bir yöntemin bulunduğu anlamına gelmez. Durum nesnede, davranış sınıfta durur; iki taraf `@bakiye` gibi bir adla birbirine bağlanır.

Bir örnek değişkeni, atanmadıkça nesnede hiç oluşmaz. Aşağıdaki dosya bunu ölçer: okunan değişken henüz atanmamıştır.

```ruby
# atanmamis.rb — atanmamis ornek degiskeni nil okunur ve listede yoktur
class Hesap2
  def oku = @yok
  def yaz(deger) = @yok = deger
end

nesne = Hesap2.new
p nesne.oku
p nesne.instance_variables
nesne.yaz(5)
p nesne.oku
p nesne.instance_variables
```

```
nil
[]
5
[:@yok]
```

Atanmamış değişken `nil` olarak okundu, ama `instance_variables` listesine girmedi. Atama yapıldıktan sonra değişken hem okundu hem listelendi. Okumak nesnede bir şey oluşturmaz; yalnız atama oluşturur. Bu davranış `Hesap2` gibi bir sınıfın nesneleri arasında bir fark da doğurur: yalnız `yaz` çağrılmış bir nesne `@yok` değişkenini taşır, çağrılmamış bir nesne taşımaz. İki nesne aynı sınıftan gelse de örnek değişkenlerinin listesi aynı olmak zorunda değildir; liste, sınıfın değil, nesnenin geçmişini yansıtır. Hata vermeyen bir okuma bu yüzden yazım hatasını gizleyebilir: `@bakyie` yazılırsa Ruby sessizce `nil` verir.

## İki Nesne, Tek Sınıf

Durumun nesnede, davranışın sınıfta durması iki nesneyle ölçülür. Aşağıdaki dosya aynı sınıftan iki hesap üretir ve iki şeyi karşılaştırır: yöntemin sahibini ve örnek değişkenlerinin adlarını.

```ruby
# iki.rb — iki nesne ayni yontemi paylasir, degerleri paylasmaz
class Hesap
  def initialize(sahip, bakiye)
    @sahip = sahip
    @bakiye = bakiye
  end

  def bakiye_metni = "#{@sahip} #{@bakiye}"
end

a = Hesap.new("ece", 100)
b = Hesap.new("can", 50)
puts a.bakiye_metni
puts b.bakiye_metni
p a.method(:bakiye_metni).owner == b.method(:bakiye_metni).owner
p a.instance_variables == b.instance_variables
p Hesap.instance_of?(Class)
p a.class
```

```
ece 100
can 50
true
true
true
Hesap
```

Aynı yöntem iki nesnede iki ayrı sonuç verdi: `ece 100` ve `can 50`. İki nesnenin yönteminin sahibi aynıdır, ikisinin örnek değişkenlerinin adları da aynıdır; ayrılan yalnız değerlerdir. Yöntem gövdesi bir kez, sınıfta yazılır ve her çağrıda çağrıyı alan nesnenin değişkenlerini okur. `Hesap.instance_of?(Class)` `true` verdi: `class Hesap` satırı `Hesap` adında bir sabit kurar ve sabitin gösterdiği şey `Class` türünde bir nesnedir. `a.class` bu nesneyi geri döndürür. Sınıf, yöntemlerin durduğu yer olduğu kadar, nesneleri üreten bir nesnedir de.

Bu ayrımın pratik bir sonucu vardır. Yöntem sayısı nesne sayısıyla çoğalmaz: bin hesap yaratıldığında `bakiye_metni` yine tek bir yerde, tek bir kez durur. Nesne başına çoğalan şey yalnız örnek değişkenlerinin değerleridir. Bir yöntemin yazanını sormak bu yüzden hep sınıfa ya da zincire yönelir; nesnenin kendisi yalnız durumu taşır ve yazanı soran ölçüm onun değişkenlerine bakmaz.

## initialize Özeldir ve Argüman Sayısını Denetler

`Hesap.new("ece", 100)` çağrısı yeni bir nesne üretir ve `initialize` yöntemini bu nesnede çağırır. `initialize`'ı çağıran `new`'dir, dışarıdaki kod değil. Sınıf bunu özel yöntem olarak saklar; kod `hesap.initialize(...)` yazarak onu çağıramaz. Aşağıdaki dosya çekirdekteki `Hesap`'ın tam tanımını kullanır, çünkü özel yöntem `komisyon` da burada ölçülür.

```ruby
# ozel.rb — initialize ozel bir yontemdir
require_relative "yazan"

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

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

  private

  def komisyon = 2
end

hesap = Hesap.new("ece", 100)
p hesap.respond_to?(:initialize)
p Hesap.private_instance_methods(false).sort
p yazan(hesap, :initialize)
begin
  Hesap.new("a")
rescue ArgumentError => e
  puts e.class
end
```

```
false
[:initialize, :komisyon]
["programci", "Hesap, def govdesi"]
ArgumentError
```

`respond_to?(:initialize)` `false` verdi: özel yöntem dışarıdan sorulduğunda görünmez. `private_instance_methods(false)` sınıfın iki özel yöntemini listeledi: `initialize` ve `komisyon`. `initialize` için `yazan` programcıyı buldu, çünkü gövdeyi sınıfta bir `def` yazdı. `Hesap.new("a")` tek bir argümanla çağrıldığında `ArgumentError` verdi: `initialize` iki argüman bekler ve Ruby sayıyı çağrı anında denetler.

**SM3.** Bu derste "argüman sayısı", bir yöntemin kabul ettiği argümanların sayısıdır; `Hesap.new` bu sayıyı `initialize` tanımından alır. `initialize(sahip, bakiye)` iki argüman ister; `Hesap.new("a")` bu yüzden `ArgumentError` verdi ve hiçbir nesne döndürmedi. Ruby sayıyı gövde koşmadan önce denetler, `@sahip` ataması da yapılmadı. Argüman sayısı böylece nesnenin ilk durumunu da sınırlar: iki örnek değişkenini kuran yapıcı, iki değer verilmeden çalışmaz. `initialize` içinde atanmayan bir değişken, sonradan başka bir yöntem tarafından atanmadıkça o nesnede hiç var olmaz; sınıfın yapıcısı bu yüzden nesnenin ilk durumunu kurduğu yerdir ve değişken adlarını bir arada görmek için en güvenilir noktadır.

Sınıfı yeniden açmak, aynı sınıf adıyla bir `class` bloğu daha yazmaktır. Mevcut tanım silinmez, yenisi eklenir (M12/K03). Bir sınıfın tanımı bu yüzden tek bir yerde olmak zorunda değildir; aşağıdaki ölçüm sınıfı yeniden açar.

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

Aynı sınıfta, aynı sahipte iki yazan bir arada bulunabilir. `Hesap` yeniden açılır ve `attr_reader :sahip` eklenir. `attr_reader` bir okuyucu yöntem üretir: `sahip` yöntemi `@sahip`'i döndürür. Önceki kurslarda (M08/K01) derleyicinin bir sınıfa ne eklediği ölçülmüştü. Burada soru farklıdır: sınıfta duran iki yöntemden birini bir `def` yazdı, ötekini bir çağrı üretti.

```ruby
# yeniden.rb — ayni sinifta iki yazan: def ve attr_reader
require_relative "yazan"

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

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

class Hesap
  attr_reader :sahip
end

hesap = Hesap.new("ece", 100)
p yazan(hesap, :sahip)
p yazan(hesap, :bakiye_metni)
p Hesap.instance_methods(false).sort
[:sahip, :bakiye_metni].each do |ad|
  tanim = Hesap.instance_method(ad)
  puts "#{ad} #{tanim.owner} #{tanim.source_location.nil?} #{RubyVM::InstructionSequence.of(tanim).nil?}"
end
```

```
["kod", "Hesap, ruby govdesi yok"]
["programci", "Hesap, def govdesi"]
[:bakiye_metni, :net, :sahip]
sahip Hesap false true
bakiye_metni Hesap false false
```

`yazan(hesap, :sahip)` kodu, `yazan(hesap, :bakiye_metni)` programcıyı buldu. İkisinin sahibi de `Hesap`'tır ve ikisi de `instance_methods(false)` listesinde görünür: `[:bakiye_metni, :net, :sahip]`. Sahibi bilmek yazanı söylemez. Son döngü ayrımın nereden geldiğini gösterir. `source_location` iki yöntem için de `nil` değildir: `attr_reader` çağrısının satırı kaynak yeri olarak kayıtlıdır. Ayrımı `iseq` yapar. `def` ile yazılmış `bakiye_metni` için `iseq` vardır, `attr_reader` ile üretilmiş `sahip` için `nil`'dir. Kural bu yüzden kaynak yerine değil `iseq`'e bakar.

Bu ölçüm, sahibin yazanı belirlemediğini gösterir: iki yöntem aynı sahipte durdu ve iki ayrı yazanla çıktı. Aynı ölçüm sınıfın bir kimlik kartı olmadığını da gösterir. `Hesap` tek bir yazan taşımaz; her yöntemin yazanı ayrı ölçülür. Sınıf düzeyinde "bu sınıfı programcı yazdı" diyen bir kategori yoktur.

## Özet

- Kursun ölçüsü, bir yöntemi kimin yazdığıdır: yazan programcı, ata, kod ya da kimse olabilir ve kararı `yazan.rb` içindeki kural verir.
- Dokuz gözlemin 2'sini programcı, 3'ünü ata yazdı; 3'ünü kod üretti; 1'inde tanım yoktu ve `method_missing` yakaladı.
- Örnek değişkenleri nesnede, yöntemler sınıfta durur; okuyucu yoksa `hesap.bakiye` `NoMethodError` verir, atanmamış değişken `nil` okunur ve `instance_variables`'a girmez.
- `initialize` özel bir yöntemdir ve argüman sayısını denetler; `Hesap.new("a")` `ArgumentError` verir.
- Bu dersin yazanı programcıdır: `durum okuma` satırında `bakiye_metni`'ni `Hesap`'ın içinde bir `def` yazdı.
- Aynı sınıfta `def` ile yazılmış yöntem programcı, `attr_reader` ile üretilmiş yöntem kod çıkar; ikisinin sahibi aynıdır ve ayrımı `iseq` yapar.

## Sonraki Adım

`Hesap` içinde `bakiye_metni` yazılı, ama `Vadeli` adlı boş bir alt sınıf da aynı yöntemi çağırıp yanıt alabiliyor. Alt sınıfta tek satır tanım yok. Yöntemi alt sınıfa kim getiriyor ve aynı yöntemi alt sınıfta yeniden yazmak neyi değiştiriyor? Sonraki ders, Kalıtım ve `super`, bunu ölçer.
