Ders 02 / 12
Nesne Modeli Sezgisi
Sayıların, nil'in ve true'nun da nesne olması; 5 + 3'te alıcının 5, yanıtlayanın Integer olması; sınıfların kendilerinin de nesne olduğu ve kendi iletilerini başka bir sınıfın yanıtladığı.
Yayın Güncelleme
Bu derste
- Sayı, nil ve true gibi değerlerin de bir sınıfa ait nesneler olduğunu koşumla gösterir
- Bir iletide alıcıyı yanıtlayandan ayırır ve 5 + 3 örneğinde yanıtlayanı sahip ölçümüyle bulur
- Sınıfların da nesne olduğunu ve onlara gönderilen iletiyi başka bir sınıfın yanıtladığını ölçer
İçindekiler
Önceki ders her işlemin bir alıcıya gönderilen ileti olduğunu kurdu ve 5 + 3’ün +‘sının Integer sınıfında tanımlı olduğunu on iki gözlemin içinde gösterdi. Bu cümle bir şeyi varsayıyordu: 5 bir nesnedir ve bir sınıfa aittir. Bu ders o varsayımı ölçer. JavaScript Temelleri kursunda (M06/K01) ilkel tipler ölçülmüştü; Ruby’de aynı soru başka biçimde sorulur: bir değere ileti gönderilebiliyorsa, o iletiyi kim karşılıyor?
Nesne yönelimi M08’de Java ile kurulmuştu ve nesne ile sınıf kavramları orada tanımlandı. Burada yeni olan, Ruby’de sayıların, nil’in ve doğruluk değerlerinin de aynı modelin içinde olmasıdır. Ayrıca bu derste çekirdekten tek bir satır alınır: tam sayi toplami.
Her Değer Bir Nesnedir
Ruby’de ilkel diye ayrı bir değer sınıfı yoktur. Aşağıdaki dosya sekiz farklı değere aynı iletiyi gönderir: class. Her satırda değerin gösterimi ve sınıfı yan yana basılır.
# degerler.rb — her degerin bir sinifi vardir
degerler = [5, 3.5, "ad", :ad, nil, true, [1], 1..2]
degerler.each do |deger|
printf("%-6s %s\n", deger.inspect, deger.class)
end
5 Integer 3.5 Float "ad" String :ad Symbol nil NilClass true TrueClass [1] Array 1..2 Range
Sayının, ondalık sayının, dizginin, sembolün, nil’in, doğruluk değerinin, dizinin ve aralığın hepsi bir sınıftan gelir. nil ve true bir sınıfın tek örneğidir: NilClass ve TrueClass. Bu, değerlere ileti gönderilebileceği anlamına gelir. Aşağıdaki dosya sayıya, true’ya ve nil’e birer ileti gönderir ve her yöntemin sahibini sorar.
# ilkel_iletiler.rb — sayi, true ve nil iletileri yanitlar puts 5.times.class puts 5.method(:times).owner puts true.method(:&).owner puts nil.method(:to_a).owner p nil.to_a p true & false
Enumerator Integer TrueClass NilClass [] false
5.times bir yineleyici döndürür ve sahibi Integer’dır. true & false bir işleç gibi görünür ama TrueClass’ta tanımlı bir yöntemdir. nil.to_a boş diziyi verir ve sahibi NilClass’tır. Üç iletinin üçü de alıcının kendi sınıfında yanıt buldu.
DT5. Bu derste “nesne”, üzerine ileti gönderilebilen her değerdir. Bir değerin bellekte nasıl tutulduğu ölçülmez; yalnız davranış, yani hangi iletiye kimin yanıt verdiği ölçülür. object_id ve bellek adresi taşıyan inspect çıktıları bu yüzden basılmaz.
Alıcı ve Yanıtlayan
5 + 3 ifadesinde alıcı 5’tir, ileti +’dır, argüman 3‘tür. M11/K04’te işlemin hedef nesnesinin hangisi olduğu ölçülmüştü. Burada alıcı bellidir; soru, alıcıya gelen iletiyi hangi tanımın yanıtladığıdır. 5 + 3’te alıcı 5’tir, yanıtlayan Integer’dır.
# alici_yanitlayan.rb — 5 + 3'te alici ile yanitlayan ayri seylerdir alici = 5 puts defined?(alici + 3) puts alici.method(:+).owner puts alici.method(:+).owner.instance_of?(Class) puts alici + 3
method Integer true 8
defined? ifade için "method" döndürdü: ortada gerçekten bir ileti var. Yöntemin sahibi Integer’dır ve Integer bir Class örneğidir; kursun sınıflandırma kuralı bu durumda yanıtlayanı sınıf sayar. Alıcı bir değerdir, yanıtlayan bir tanımdır. İkisi çoğu zaman aynı adı taşımaz: 5 bir değerdir, Integer onun ait olduğu sınıftır.
Bu satır, kursun ortak çekirdeğindeki tam sayi toplami gözlemidir ve çekirdeğin tamamında olduğu gibi yanıtlayanı sınıftır. Bu derste yanit.rb çekilmez; defined? sonucu ve yöntemin sahibi doğrudan basılır. Kural aynıdır: önce defined?, sonra sahip.
Sınıflar da Nesnedir
Her değerin bir sınıfı olduğuna göre Integer‘ın da bir sınıfı olmalıdır. Integer Ruby’de bir sabittir ve bir nesneyi gösterir; o nesne de bir iletiyi yanıtlayabilir. Peki Integer’a bir ileti gönderildiğinde kim yanıtlar? Aşağıdaki dosya 5’in ve Integer’ın sınıfını, ardından Integer’a gönderilen === iletisinin sahibini basar.
# siniflar_nesne.rb — siniflar da nesnedir, kendi iletilerini baska bir sinif yanitlar puts 5.class puts Integer.class puts Integer.method(:===).owner puts Integer.method(:===).owner.instance_of?(Class) puts Integer === 5 puts Integer === "5"
Integer Class Module true true false
5’in sınıfı Integer’dır; Integer’ın sınıfı Class’tır. Yani Integer kendi başına bir nesnedir ve ona gönderilen === iletisini Integer değil Module yanıtlar. Module da bir sınıftır, bu yüzden kural yanıtlayanı yine sınıf sayar. Integer === 5 doğru, Integer === "5" yanlış döner: === burada “bu değer bu sınıfa ait mi” sorusunu sorar.
İleti Alıcının Sınıfına Bağlıdır
Her değerin bir sınıfı olması, her değerin her iletiyi yanıtlayamayacağı anlamına gelir. times iletisi Integer’da tanımlıdır; nil’in sınıfı NilClass’ta ise yoktur. Aşağıdaki dosya üç iletiyi iki alıcıya sorar ve nil’e times gönderildiğinde ne olduğuna bakar.
# ileti_yoklama.rb — ileti alicinin sinifina baglidir
[5, nil].each do |alici|
printf("%-4s times: %-5s to_a: %-5s to_s: %s\n", alici.inspect, alici.respond_to?(:times), alici.respond_to?(:to_a), alici.respond_to?(:to_s))
end
begin
nil.times
rescue NoMethodError => e
puts e.class
end
5 times: true to_a: false to_s: true nil times: false to_a: true to_s: true NoMethodError
respond_to? bir nesnenin bir iletiyi yanıtlayıp yanıtlamadığını sorar. 5 times ve to_s iletilerini yanıtlar; nil ise to_a ve to_s iletilerini yanıtlar, times iletisini yanıtlamaz. Yanıtlanmayan iletiyi göndermek NoMethodError fırlatır ve burada yalnız istisnanın sınıfı basıldı. Yani “her şey nesnedir” cümlesi “her nesne her iletiyi alır” anlamına gelmez: iletinin karşılığı alıcının sınıfında ya da o sınıfın üst tanımlarında bulunmalıdır.
Her Şeyin Nesne Olduğunu Yoklamak
Önceki bölümler nesne modelini alıcı, yanıtlayan ve sınıf üzerinden kurdu. Bu bölüm “her şey nesnedir” savını sayılar, nil ve sınıfların kendisi üzerinde tek tek sınar. Savın sınırı da ölçülür: bir değerin hangi türden olduğunu soran iki sorgu, instance_of? ve is_a?, aynı soruyu sormaz. Eksen yine yanıtlayandır; sorgulardan birinin sahibi çıktının son satırında okunur.
# her_sey_nesne.rb — nesne olmayan deger yoktur puts 5.respond_to?(:+) puts nil.respond_to?(:to_a) puts 5.frozen? puts 5.methods.size > 50 puts Integer.class puts Class.class puts 5.instance_of?(Integer) puts 5.is_a?(Comparable) puts 5.instance_of?(Comparable) puts 5.method(:frozen?).owner
true true true true Class Class true true false Kernel
İlk iki satır true verdi: 5 toplama iletisini karşılar, nil ise to_a iletisini karşılar. 5.frozen? de true döndü; sayı değişmez bir nesnedir. 5.methods.size > 50 sorusu true oldu. Burada sayının kendisini bilerek basmadık: bir nesnenin kaç yöntemi olduğu sürüme göre değişir, ancak “epeyce yöntemi olduğu” sonucu değişmez. Beşinci ve altıncı satırlar bölümün asıl bulgusudur: Integer.class ve Class.class ikisi de Class bastı. Integer bir sınıftır ve bir nesnedir; o nesnenin sınıfı Class’tır. Class’ın kendisi de bir nesnedir ve onun da sınıfı Class’tır. Zincir kendi üzerine kapanır, yani “sınıfın sınıfı nedir” sorusu sonsuza gitmez.
Son üç sorgu iki yöntemin farkını gösterir. 5.instance_of?(Integer) true, 5.is_a?(Comparable) true, 5.instance_of?(Comparable) ise false verdi. instance_of? yalnız nesnenin doğrudan sınıfını sorar; is_a? ise nesnenin o türden sayılıp sayılmadığını, yani daha geniş bir soruyu sorar. Zincirin nasıl işlediğini burada açmıyoruz; yalnız farkı kaydediyoruz. Son satırdaki sahip Kernel çıktı: 5.frozen? iletisini sayının sınıfı değil, her nesnenin ortak aldığı bir modül karşılar.
DT6. > 50 eşiği bir seçimdir: ölçüm yalnızca sayının eşikten büyük olduğunu gösterir ve sayının kendisi dilin sözü değildir.
# nil_nesne.rb — nil de bir nesnedir ve sahibi vardir puts nil.class puts nil.to_a.inspect puts nil.to_s.empty? puts nil.method(:to_a).owner puts true.class puts true.method(:&).owner
NilClass [] true NilClass TrueClass TrueClass
nil de bir nesnedir: nil.class NilClass verdi. nil.to_a boş bir dizi döndürdü ve nil.to_s boş bir dizgi verdiği için empty? sorusu true oldu. Yani “değer yok” durumu bile iletilere yanıt verir ve bu yüzden nil üzerinde to_a çağırmak hata değil, [] üretir. nil.method(:to_a).owner NilClass çıktı: yanıtlayan nil’in kendi sınıfıdır. true için de aynı düzen geçerlidir: sınıfı TrueClass, & iletisinin sahibi de TrueClass. true ile nil‘in sınıfları ayrıdır ve ölçülen iki iletinin sahibi de bu iki sınıfın kendisi çıktı. Bu iki bölümün ortak sonucu, dersin başındaki sezgiyi sıkılaştırır: Ruby’de hiçbir değer iletiye kapalı değildir. Bir ifadenin değeri ne olursa olsun, sonuna bir ileti yazılabilir ve yanıtlayan bir sınıf ya da modül olarak bulunabilir.
Zincirleme İletiler ve Dönüş Nesnesi
Her ileti bir nesne döndürür ve döndürülen nesne bir sonraki iletinin alıcısı olur. Bu bölüm, zincirin her halkasında yanıtlayanın değişebildiğini ölçer. Ölçülen ifade 5.to_s.size.to_f gibi bir zincirdir; zinciri soldan okumak gerekir: önce 5 iletiyi alır, çıkan nesne bir sonrakini alır ve böylece sürer. Eksen yine sahiptir: iki halkanın sahibi ayrı ayrı basılır.
# zincir.rb — her ileti bir nesne dondurur ve zincir surer sonuc = 5.to_s.size.to_f puts sonuc puts sonuc.class puts 5.to_s.class puts 5.to_s.size.class puts [3, 1, 2].sort.reverse.first puts "ruby".reverse.upcase.class puts 5.method(:to_s).owner puts 5.to_s.method(:size).owner
1.0 Float String Integer 3 String Integer String
Zincirin sonucu 1.0 oldu ve sınıfı Float’tır. Ara halkaları ayrı ayrı yoklayınca zincirin nasıl kurulduğu görünür: 5.to_s bir String verdi, 5.to_s.size ise bir Integer verdi. Yani aynı ifadenin içinde dört ayrı nesne ve üç ayrı sınıf vardır: 5 için Integer, "5" için String, 1 için yine Integer ve sonunda 1.0 için Float. [3, 1, 2].sort.reverse.first iki dizi halkasından sonra elemana indi ve 3 verdi; "ruby".reverse.upcase.class ise String bastı, çünkü iki halka da dizgi döndürür.
Sahip satırları, iki farklı halkanın iki farklı sınıfta tanımlı olduğunu gösterdi: 5.method(:to_s).owner Integer, 5.to_s.method(:size).owner ise String çıktı. Aynı to_s ve size adları başka bir alıcıda başka bir sahibe gidebilirdi; yanıtlayanı alıcının sınıfı belirler, zincirdeki sırası değil. Bu, ‘Ruby nesne yönelimlidir’ savının somut karşılığıdır: işlem bir nesneye bir ileti göndermektir ve cevap yine bir nesnedir. Bir zincirin okunması için her halkanın dönüş nesnesinin sınıfını bilmek gerekir; sınıf değiştiğinde bir sonraki ileti başka bir yanıtlayana gider. Bu yüzden uzun bir zincirde bir hata oluştuğunda, hatanın nerede çıktığını bulmanın yolu zinciri halka halka bölüp her ara nesnenin sınıfını yoklamaktır.
Eşitlik ve Kimlik Ayrı Sorulardır
Eşitlik ile kimlik iki ayrı sorudur. == iki nesnenin değerce eşit olup olmadığını, equal? ise ikisinin aynı nesne olup olmadığını sorar. Bu bölüm iki sorguyu aynı değerler üzerinde yan yana koyar. Eksen sahiptir: iki sorguyu karşılayan yöntemin tanımlandığı yer sonda basılır.
# kimlik.rb — esitlik ile kimlik ayri sorulardir
a = "x"
b = a
puts a.equal?(b)
puts a.equal?("x")
puts a == "x"
puts a.dup.equal?(a)
puts 1.equal?(1)
puts :s.equal?(:s)
puts nil.equal?(nil)
puts 1 == 1.0
puts 1.eql?(1.0)
puts 1.equal?(1.0)
puts a.method(:==).owner
puts a.method(:equal?).owner
true false true false true true true true false false String BasicObject
b = a atamasından sonra a.equal?(b) true verdi: iki ad tek nesneyi gösteriyor. a.equal?("x") false, a == "x" ise true oldu: metin aynı, nesne farklı. a.dup.equal?(a) de false verdi, çünkü dup yeni bir nesne üretir. Sayı, sembol ve nil ise ayrı davrandı: 1.equal?(1), :s.equal?(:s) ve nil.equal?(nil) üçü de true verdi; bu değerlerin her yazımı aynı nesnedir.
Sayıların kendi içindeki karşılaştırmada üç sorgu iki farklı cevap verdi. 1 == 1.0 true oldu, 1.eql?(1.0) ile 1.equal?(1.0) ise false verdi. Yani == değerlere bakar ve tam sayı ile kesirli sayı arasında eşitlik görür; eql? ve equal? ise daha katı sorular sorar. Sahip satırları iki sorgunun tanımlandığı yeri gösterdi: == için String, equal? için BasicObject çıktı. Yani == her sınıfın kendi tanımına bırakılmış bir sorudur, equal? ise tek bir ortak yerde tanımlıdır. Bu yüzden bir değerin “aynı” olup olmadığını sormadan önce hangi anlamda sorulduğunu belirtmek gerekir. Sonuç olarak equal? kimlik sorusu için, == değer sorusu için kullanılır; ikisini karıştırmak, iki ayrı nesneyi aynı sanmanın ya da aynı nesneyi iki sanmanın yolunu açar.
Sınırlayıcı Ölçüm
Bu dersin ölçümlerinin çoğunda yanıtlayan sınıftır; class ve frozen? için Kernel modülü, equal? için BasicObject istisnadır. “Sınıf yanıtlayanı” ifadesi ise iki farklı şeye kayabilir. Kayma şöyle olur: Integer.=== iletisini yanıtlayan Integer zannedilebilir. Yukarıdaki koşum bunun yanlış olduğunu gösterdi. Yanıtlayan alıcının kendisi değil, alıcının ait olduğu sınıf zinciridir: alıcı 5 ise Integer, alıcı Integer ise Class zincirinde Module.
# alicinin_sinifi.rb — yanitlayan alicinin kendisi degil, ait oldugu siniftir
[5, "ad", Integer, String].each do |alici|
printf("%-8s sinifi %-8s class sahibi %s\n", alici.inspect, alici.class, alici.method(:class).owner)
end
5 sinifi Integer class sahibi Kernel "ad" sinifi String class sahibi Kernel Integer sinifi Class class sahibi Kernel String sinifi Class class sahibi Kernel
class iletisinin sahibi dört alıcıda da Kernel’dir; çünkü bu ileti her nesnede ortak bir modülde tanımlıdır. Alıcıların sınıfları ise farklıdır. “Bu iletiyi kim yanıtlıyor” sorusunun iki katmanı vardır: alıcı hangi sınıfa ait ve o sınıf iletiyi kendi mi yanıtlıyor. Bu derste ölçülen birinci katmandır. İkincisinin, yani yöntemin alıcının sınıfında değil bir üst tanımda bulunduğu durumların ölçümü modül yanıtlayanını konu alan Yöntemler dersinde sürer.
Özet
- Ruby’de sayılar,
nil,true, dizgiler, semboller, diziler ve aralıklar birer nesnedir; her birinin bir sınıfı vardır. 5 + 3’te alıcı5’tir ve yanıtlayanInteger’dır; alıcı bir değerdir, yanıtlayan bir tanımdır.5.times,true & falsevenil.to_ailetilerini sırasıylaInteger,TrueClassveNilClassyanıtlar.- Sınıflar da nesnedir:
Integer.classClass’tır veInteger.===iletisiniModuleyanıtlar. - Bu dersin yanıtlayanı çoğunlukla sınıftır: iletiyi alıcının kendisi değil, alıcının ait olduğu sınıf karşılar.
classvefrozen?gibi her nesnede ortak iletilerin sahibi iseKernelmodülüdür. Integer.classveClass.classikisi deClassverir;equal?kimliği,==değeri sorar ve zincirdeki her halkanın yanıtlayanı o halkanın alıcısının sınıfıdır.
Sonraki Adım
Nesnelerin bir sınıfı olduğu ve iletinin onlara gönderildiği görüldü. Peki bir ad bir nesneye nasıl bağlanıyor ve x = 5 yazıldığında kim çalışıyor? Atamanın bir ileti olup olmadığı, ad kuralları ve kapsam işaretleri sonraki dersin, Değişkenler ve Sabitler’in konusudur.
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.