---
title: 'Modüller ve Karışımlar'
source: 'https://academia.sh/tr/kurslar/ruby-nesne-yonelimi/moduller-ve-karisimlar'
course: 'Nesne Yönelimi ve Metaprogramlama'
language: tr
updated: '2026-10-21T05:03:42+00:00'
license: 'CC BY-SA 4.0'
objectives:
    - 'Bir modülün include ile sınıfın ata zincirine girdiğini ancestors ve include? ile gösterir'
    - 'Modülün örneği olmadığını ve new çağrısının NoMethodError verdiğini ölçer'
    - 'Aynı adı taşıyan iki modülde zincirdeki sıranın sonucu belirlediğini gösterir'
    - 'Tek bir nesneye extend ile modül karıştırmanın diğer nesneleri etkilemediğini ölçer'
---

# Modüller ve Karışımlar

Modülün sınıftan farkı; include ile modülü ata zincirine karıştırmak; Comparable ile <=>; aynı adlı iki modülde sıra; tek nesneye extend.

Önceki ders `Hesap` sınıfında `rapor` yazılmadığı hâlde `hesap.rapor` çağrısının `rapor: ece 100` verdiğini bıraktı. Zincirde `Hesap` ile `Object` arasında duran `Raporlanir` yöntemi taşıyor, ama bir modülün bir sınıfa yöntem vermesi tam olarak ne demek? Modül sınıf değilse zincire nasıl girer?

Bu dersin yazanı yine ata yazanıdır, ama atanın türü değişir: tanım bir üst sınıfta değil, karıştırılmış bir modülde durur. Ders önce modülü sınıftan ayırır, sonra karışımın zincirdeki yerini ve sırasını ölçer.

## Karıştırılmış Modül Yazan Olur

Aşağıdaki dosya `Raporlanir` modülünü `Hesap` sınıfına `include` ile karıştırır. `yazan.rb` önceki derslerdeki kuraldır ve burada da aynen yüklenir.

```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
# karisim.rb — include ile karistirilan modulun yazani
require_relative "yazan"

module Raporlanir
  def rapor = "rapor: #{@sahip} #{@bakiye}"
end

class Hesap
  include Raporlanir

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

hesap = Hesap.new("ece", 100)
puts hesap.rapor
p yazan(hesap, :rapor)
p Hesap.include?(Raporlanir)
p Object.include?(Raporlanir)
p Hesap.ancestors
```

```
rapor: ece 100
["ata", "Raporlanir"]
true
false
[Hesap, Raporlanir, Object, Kernel, BasicObject]
```

`yazan` ata yazanını ve kanıt olarak `Raporlanir`'i verdi: tanım `Hesap`'ın gövdesinde değil, modülde yazılı. `Hesap.include?(Raporlanir)` doğru, `Object.include?(Raporlanir)` yanlış çıktı; karışım yalnız `include` çağrısını alan sınıfı etkiler. `Hesap.ancestors` listesinde `Raporlanir`, `Hesap` ile `Object` arasında durur. Bir sınıfa modül karıştırmak, modülü o sınıfın hemen üstüne yerleştirmektir.

**SM7.** Bu derste "karıştırmak", `include` çağrısıyla bir modülü bir sınıfın ata zincirine, sınıfın hemen üstüne yerleştirmektir. Bunu yapan kod satırı `include Raporlanir`, sonuç ise zincirdeki yeni yerdir. Karışımın yazanı, kuralın diliyle ata çıkar, çünkü tanım alıcının sınıfında değil zincirin üst kısmındadır.

Karışımın zincire bir halka eklediği, yöntemleri sınıfa kopyalamadığı iki ek soruyla görülür. `include` bir ifadedir ve bir değer döndürür; `instance_methods(false)` sınıfın kendi gövdesini sorar. Aşağıdaki dosya ikisini ölçer.

```ruby
# kopya_yok.rb — include zincire halka ekler, yontemi kopyalamaz
module Raporlanir
  def rapor = "rapor"
end

class Hesap
  p(include(Raporlanir))
end

p Hesap.instance_methods(false)
p Raporlanir.instance_methods
p Hesap.instance_method(:rapor).owner
p Hesap.included_modules.take(2)
```

```
Hesap
[]
[:rapor]
Raporlanir
[Raporlanir, Kernel]
```

`include` çağrısı alıcıyı, yani `Hesap` sınıfını döndürdü. `Hesap.instance_methods(false)` boş çıktı: `rapor` sınıfın gövdesine taşınmadı, modülde kaldı. `Raporlanir.instance_methods` yöntemi modülün kendi listesinde gösterdi ve `instance_method(:rapor).owner` sahibin `Raporlanir` olduğunu doğruladı. `included_modules` zincirdeki modülleri `Raporlanir`, `Kernel` sırasıyla verdi. Yani önceki dersteki boş alt sınıfla aynı durum geçerlidir: çağrılabilir olmak ile sınıfa yazılı olmak ayrı şeylerdir, ve modül yazılı olanın yerini değiştirmez, zincirin uzunluğunu değiştirir.

## Modül Sınıf Değildir

Karışım modülü sınıfa benzetir ama ikisi aynı şey değildir. Bir modülden nesne üretilemez; modül yalnız başkalarının zincirine girmek için vardır. Aşağıdaki dosya bunu iki soruyla ölçer.

```ruby
# modul_sinif_degil.rb — modulun ornegi olmaz
module Raporlanir
  def rapor = "rapor"
end

p Raporlanir.instance_of?(Class)
p Raporlanir.instance_of?(Module)
begin
  Raporlanir.new
rescue NoMethodError => e
  puts e.class
end
```

```
false
true
NoMethodError
```

`Raporlanir` bir `Module` örneğidir, `Class` örneği değildir ve `new` yöntemi onda tanımlı değildir. Bu fark karışımın neden gerekli olduğunu da açıklar: kalıtım yalnız bir üst sınıf kabul eder, oysa bir sınıfa istenen sayıda modül karıştırılabilir. Birbirinden bağımsız davranışlar, örneğin raporlama ile karşılaştırma, tek bir üst sınıf zincirine sığdırılmaya çalışılmaz; ayrı modüllerde yazılır ve ihtiyaç duyan sınıfa karıştırılır.

## Çekirdekten Bir Karışım: Comparable

Karışım yalnız kendi yazdığımız modüllerle sınırlı değildir. Çekirdekteki `Comparable` modülü, sınıfa yalnız `<=>` yazıldığında karşılaştırma yöntemlerinin geri kalanını verir. Aşağıdaki dosya küçük bir `Puan` sınıfı üzerinde bunu ölçer.

```ruby
# karsilastir.rb — Comparable ile <=>
require_relative "yazan"

class Puan
  include Comparable

  attr_reader :deger

  def initialize(deger)
    @deger = deger
  end

  def <=>(diger) = deger <=> diger.deger
end

a = Puan.new(3)
b = Puan.new(5)
c = Puan.new(9)
p c.between?(a, c)
p b > a
p [c, a, b].min.deger
p yazan(a, :between?)
p yazan(a, :<=>)
```

```
true
true
3
["ata", "Comparable"]
["programci", "Puan, def govdesi"]
```

`Puan` içinde yalnız `<=>` yazılı, ama `between?` ve `>` çağrıları ile `<=>` üzerinden çalışan `min` çağrısı sonuç verdi. `between?` için yazan ata ve kanıt `Comparable`; `<=>` için programcı, çünkü onu `Puan` kendi gövdesinde yazdı. Aynı sınıfta iki yöntem, iki ayrı yazan: karışım sınıfın kendi tanımını silmez, yanına ekler.

`Comparable` yalnız `between?` değil, `<=>` üzerinden kurulan bütün karşılaştırmaları verir. Aşağıdaki dosya `Puan` sınıfında hiç yazılmayan iki davranışı daha ölçer: eşitlik ve aralığa sıkıştırma.

```ruby
# karsilastir_ek.rb — Comparable uc yontemi daha saglar
require_relative "yazan"

class Puan
  include Comparable

  attr_reader :deger

  def initialize(deger)
    @deger = deger
  end

  def <=>(diger) = deger <=> diger.deger
end

a = Puan.new(3)
b = Puan.new(5)
p a == Puan.new(3)
p a.equal?(Puan.new(3))
p Puan.new(9).clamp(a, b).deger
p yazan(a, :==)
p Puan.ancestors.take(3)
```

```
true
false
5
["ata", "Comparable"]
[Puan, Comparable, Object]
```

`a == Puan.new(3)` doğru verdi, çünkü `Comparable#==` iki nesneyi `<=>` sonucu sıfır mu diye sınar. Oysa `equal?` iki ayrı nesne olduklarını gösterip yanlış verdi: eşitlik değere göre, kimlik nesneye göre sorulur. `clamp` dokuz değerini beşe indirdi. `yazan` `==` için `Comparable`'ı gösterdi; `BasicObject` de bir `==` tanımlar, ama zincirde `Comparable` önce geldiği için o gölgelenir. Bu, aşağıda ölçülen sıra kuralının çekirdek örneğidir.

## Aynı Ad İki Modülde

Bir sınıfa iki modül karıştırılırsa ve ikisi de aynı adı tanımlıyorsa hangisi kazanır? Zincirdeki sıra bunu belirler, ve karıştırma sırası zincirin sırasını belirler: sonradan karıştırılan, öncekinin önüne geçer.

```ruby
# iki_modul.rb — aynı ad, iki modul
module A1
  def ad = "A1"
end

module B1
  def ad = "B1"
end

class C1
  include A1
  include B1
end

p C1.ancestors.take(3)
puts C1.new.ad
p C1.ancestors.count(B1)
```

```
[C1, B1, A1]
B1
1
```

`include A1` önce, `include B1` sonra yazıldı ve zincir `[C1, B1, A1]` çıktı: son karıştırılan, alıcıya en yakın durur. `ad` çağrısı bu yüzden `B1` verdi. `ancestors.count(B1)` bir verdi: modül zincirde tek halka olarak durur. Aynı modülü ikinci kez karıştırmak bu tabloyu değiştirir mi, aşağıdaki ölçüm sorar.

```ruby
# tekrar_include.rb — ikinci include zinciri degistirmez
module A1
  def ad = "A1"
end

module B1
  def ad = "B1"
end

class C1
  include A1
  include B1
  include A1
end

p C1.ancestors.take(3)
puts C1.new.ad
p C1.ancestors.count(A1)
```

```
[C1, B1, A1]
B1
1
```

Sondaki ikinci `include A1` zincirde bir şey kıpırdatmadı. `A1` zaten zincirde olduğu için Ruby onu yeniden öne almadı, bu yüzden `B1` kazanmaya devam etti ve `ancestors.count(A1)` bir verdi. Karışım zincirde bir modülü en çok bir kez barındırır; sırayı değiştirmenin yolu yeniden `include` etmek değildir.

**SM8.** Bu derste "zincirdeki sıra", `ancestors` dizisinin soldan sağa okunuşudur ve yöntem araması bu sırayla yürür. İlk eşleşen tanım kazanır. Sonradan `include` edilen modül sınıfın hemen üstünde yer aldığı için, öncekini gölgeler.

## Modüllerde super

Modül içindeki bir yöntem de `super` çağırabilir ve `super` o anki zincirde bir sonraki halkaya iner. Bu, modülün sınıftan önce mi sonra mı geldiğine göre iki ayrı sonuç verir. Aşağıdaki dosya iki modülü bir üst sınıfla ve alt sınıfla birleştirir.

```ruby
# modul_super.rb — super zincirde modullerin icinden gecer
module A2
  def ad = "A2 " + super
end

module B2
  def ad = "B2 " + super
end

class Temel
  def ad = "temel"
end

class C2 < Temel
  include A2
  include B2
end

class D2 < Temel
  include A2
  def ad = "D2 " + super
end

puts C2.new.ad
p C2.ancestors.take(4)
puts D2.new.ad
```

```
B2 A2 temel
[C2, B2, A2, Temel]
D2 A2 temel
```

`C2` için çağrı `B2`'ye girdi, `super` ile `A2`'ye, oradan `Temel`'e indi ve metin zincir sırasıyla birleşti. `ancestors` aynı yolu `[C2, B2, A2, Temel]` olarak gösterdi: `super` zincirin üzerinde yürümektir. `D2`'de sınıf kendi `ad`'ını yazdı; modül sınıfın altında kaldığı için önce `D2` çalıştı, `super` ile `A2`'ye ve ondan `Temel`'e inildi. Önceki dersin `super_method` ölçümüyle birleştirildiğinde sonuç şudur: yeniden tanımlayan alt sınıf da, karışan modül de aynı zincirdeki halkalardır ve `super` ikisini ayırt etmez.

**SM14.** Bu derste "zincirde super", `super` çağrısının `ancestors` sırasında bir sonraki halkaya inmesidir. Halkanın sınıf mı modül mü olduğu çağrıyı değiştirmez; yalnız sıra belirleyicidir.

Modüller de birbirine karıştırılabilir. Bir modülün zinciri, içine karıştırdığı modülleri taşır ve o modülü `include` eden sınıf hepsini birden devralır. Aşağıdaki dosya iki katmanlı bir karışımı ölçer.

```ruby
# ic_ice.rb — modul icine karisan modul sinifa birlikte gelir
module Ust
  def ust = "ust"
end

module Alt
  include Ust

  def alt = "alt"
end

class Kutu
  include Alt
end

p Alt.ancestors
p Kutu.ancestors.take(3)
p Kutu.new.ust
p Kutu.include?(Ust)
```

```
[Alt, Ust]
[Kutu, Alt, Ust]
"ust"
true
```

`Alt.ancestors` modülün kendi zincirini gösterdi ve `Ust` onda `Alt`'ın hemen arkasında durdu. `Kutu` yalnız `Alt`'ı karıştırdı, ama zincirine `Ust` de girdi; `Kutu.new.ust` bu yüzden çalıştı ve `Kutu.include?(Ust)` doğru verdi. Karışımın bir halkayı değil, modülün o anki zincirinin tamamını getirdiği buradan görülür. Bir sınıfın zincirini okurken modüllerin kendi karışımlarını da hesaba katmak gerekir.

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

İki sınır var. Birincisi, sınıf kendi `rapor`'unu yazarsa karışımın tanımı ne olur? İkincisi, modül sınıfın tamamına değil, yalnız tek bir nesneye karıştırılırsa yazan ve zincir nasıl görünür?

```ruby
# sinirlayici.rb — sinif golgeler, extend tek nesneye karisir
require_relative "yazan"

module Raporlanir
  def rapor = "rapor: #{@sahip} #{@bakiye}"
end

class Hesap2
  include Raporlanir

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

  def rapor = "hesap2 raporu"
end

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

p yazan(Hesap2.new("ece", 100), :rapor)

tek = Hesap3.new("can", 50)
tek.extend(Raporlanir)
puts tek.rapor
p yazan(tek, :rapor)
p Hesap3.new("a", 1).respond_to?(:rapor)
```

```
["programci", "Hesap2, def govdesi"]
rapor: can 50
["ata", "Raporlanir"]
false
```

`Hesap2` kendi `rapor`'unu yazınca kural programcı verdi: sınıfın kendi tanımı, karıştırdığı modülden önce gelir ve onu gölgeler, `Raporlanir#rapor` zincirde durmaya devam eder ve yalnız `super` çağrısıyla ulaşılabilir. `tek.extend(Raporlanir)` ise modülü yalnız `tek` nesnesine karıştırdı; o nesne `rapor: can 50` yanıtı verdi, ata yazanı `Raporlanir` çıktı, ama aynı sınıftan yeni üretilen bir nesne `respond_to?(:rapor)` sorusuna yanlış dedi. Bu nesnenin zinciri sınıfından farklı olmuştur; o zincir nereye yerleşir sorusu ilerleyen derslerde tekil sınıf adıyla ele alınacak.

`extend`'in nesneye ne yaptığını `include`'la aynı sorularla ölçmek mümkün: nesne modülün örneği sayılır mı, sınıfın diğer nesneleri sayılır mı ve nesnenin kendine özgü zincirinde modül nerede durur?

```ruby
# extend_olcum.rb — extend yalniz o nesnenin zincirine halka ekler
module Raporlanir
  def rapor = "rapor"
end

class Hesap3
end

tek = Hesap3.new
oteki = Hesap3.new
tek.extend(Raporlanir)

p tek.is_a?(Raporlanir)
p oteki.is_a?(Raporlanir)
p tek.singleton_class.include?(Raporlanir)
p tek.singleton_class.ancestors.drop(1).take(2)
p Hesap3.include?(Raporlanir)
p oteki.singleton_class.ancestors.drop(1).take(2)
```

```
true
false
true
[Raporlanir, Hesap3]
false
[Hesap3, Object]
```

`tek` modülün örneği gibi davrandı, `oteki` davranmadı ve `Hesap3` sınıfı modülü hiç görmedi. Nesnenin kendi zincirinin başındaki halkadan sonra `Raporlanir`, ardından `Hesap3` geldi; `oteki` için aynı yerde doğrudan `Hesap3` ve `Object` var. Yani `extend` modülü sınıfın zincirine değil, o tek nesneye özgü zincire ekler. Bu zinciri tutan yapının adı tekil sınıftır ve sonraki derslerde ayrıca incelenecek.

## Karışım ile Kalıtım Arasında Seçim

Modül ile üst sınıf arasındaki seçim, ilişkinin türüne bağlıdır. `Vadeli` bir `Hesap`'tır: aynı türün özelleşmiş bir hâli. `Hesap` ise raporlanabilir bir şeydir: türün bir yeteneği. Yetenekler farklı türlerde tekrar eder, örneğin hesap da fatura da raporlanabilir, ama aynı üst sınıfı paylaşmak zorunda değildir. Yeteneği modülde yazmak, onu iki ayrı kalıtım ağacına tek yerde yazılmış olarak vermenin yoludur.

Bir kısıt da var: Ruby'de bir sınıfın tek üst sınıfı olur ama istenen kadar karışımı olabilir. Zincir bu yüzden her zaman doğrusal kalır. Modüller zincire sıralı halkalar olarak girer ve ölçümde görüldüğü gibi bu sıra tahmin edilebilir: son karıştırılan başa gelir. Birden çok üst sınıf olsaydı zincirin hangi yoldan yürüyeceği ayrı bir kural gerektirirdi; tek zincir bunu gereksiz kılar.

Karışımın bir bedeli de yazanı dağıtmasıdır. Bir sınıfın yönteminin hangi modülden geldiğini görmek için sınıfın gövdesi yetmez, `ancestors` ve `yazan` gibi ölçümlere ihtiyaç duyulur. Bu ders boyunca kullanılan `yazan.rb`'nin işi tam olarak budur: tanımın yerini, sınıfın gövdesine bakmadan, çalışan nesneye sorarak bulmak.

## Özet

- Modül bir `Module` örneğidir, `Class` örneği değildir; `Raporlanir.new` `NoMethodError` verir. `include` modülü sınıfın hemen üstüne, `Hesap` ile `Object` arasına karıştırır; `Hesap.include?(Raporlanir)` doğru, `Object.include?(Raporlanir)` yanlıştır.
- Karışımın yazanı atadır: `hesap.rapor` için kanıt `Raporlanir` çıktı; `Hesap.instance_methods(false)` ise boştur, yöntem sınıfa kopyalanmaz.
- `Comparable` karışımında yalnız `<=>` yazılır; `between?` ve `==` ata yazanıdır, `<=>` programcı yazanıdır.
- Aynı ad iki modülde varsa sonradan karıştırılan kazanır (`[C1, B1, A1]`); aynı modül zincirde bir kez durur ve ikinci `include` sırayı değiştirmez.
- Modül içinde `super` zincirde bir sonraki halkaya iner; modül içine karışan modül de sınıfa birlikte gelir (`Kutu.include?(Ust)`).
- Sınıf kendi tanımını yazarsa karışımı gölgeler ve yazan programcı olur; `extend` modülü yalnız tek nesneye karıştırır.

## Sonraki Adım

`Hesap` sınıfının `komisyon` adlı bir yöntemi var ve `hesap.komisyon` dışarıdan çağrılınca `NoMethodError` veriyor; yine de `net` yöntemi aynı nesnenin içinden onu çağırabiliyor. Yazan hâlâ programcı, ama kim çağırabiliyor ve neden bazı çağrılar reddediliyor? Sonraki ders, Görünürlük, bu soruyu ölçer.
