17
Module ve Mixin
17.1 Module nedir?
En genel anlamıyla, modül (module), programın iyi düzenlenmesini sağla- yan yapı taşlarıdır. modül, fiziksel olarak düzenlenmiş ve farklı dosyalara yazılmış Ruby kodlarının lojik olarak biraraya getirilmesini sağlar. Bir mo- dül değişkenler, yürütülebilir kodlar, fonksiyonlar, sınıflar içerebilir. İçer- diği öğeler farklı dosyalarda, farklı fiziksel ortamlarda olabilir. Bir modül bir programa çağrılınca, o modülün içindekiler, sanki o programa yazılmış gibi davranırlar.
17.2 Neden Module?
Belli bir işi yapmak için metot tanımlamayı öğrendik. Belli nitelikleri ve davranışları olan nesneler yaratmak için de sınıf (class) tanımlamayı bi- liyoruz. Bunlar programımızın istediğimiz eylemleri yapması için yararlı öğelerdir. Ama, bir programda yazdığımız metodu ya da sınıfı başka bir programda kullanmak istersek, şu ana kadar, kes-yapıştır yöntemi dışında bir yol bilmiyoruz. Çok sayıda metot ve sınıf kullanan büyük programlarda kes-yapıştır yöntemi ideal bir yöntem değildir. Onun yerine, önceden yara- tılan metotları ve sınıfları kullanmanın daha iyi bir yolu olmalıdır. Modül (module) bu kolaylığı sağlar.
Sınıfları incelerken, Bir Ruby sınıfının en çok bir tane üst sınıfı olabi- leceğini, ama istenildiği kadar alt sınıfları olabileceğini söylemiştik. Tabii, alt sınıfların da başka alt sınıfları olabilir. Böylece atadan oğula giden sa-
çaklar oluşur.
Ancak, uygulamada bir sınıfta, daha önce başka yerde tanımlanmış olan metotları çağırıp kullanmak gerekir. Tabii, kullanılacak metotların tek- rar sözkonusu sınıfta yeniden tanımlanması mümkündür. Ama bu yol çok sayıda tekrarı getirir ve nesne tabanlı programlamanın özüne aykırı düşer.
Dolayısıyla, daha önce tanımlanmış metotları ve sabitleri tekrar tek- rar tanımlamadan kullanmanın bir yolu bulunmalıdır. C++ dilinde bu so- run, bir sınıfın birden çok üst sınıfı olmasına izin verilerek aşıldı. Ancak, bir sınıfın üst sınıflarından ve alt sınıflarından oluşan atalar ve oğullar, karmaşık bir düğümler kümesi yarattı. Bu yapı, C++ programcısına yeni zorluklar getirdi.
Java dili, bu sorunu aşmak için arayüzleri (interface) kullandı. Java’nın bulduğu yöntem C++ dilinde karşılaşılan zorlukları kolayca aştığı için geniş kabul gördü. Ondan sonra ortaya çıkan nesne tabanlı diller, sorunu benzer yöntemlerle çözme yoluna gittiler. Ruby’nin getirdiği çözüm yolu, düşünce olarak Java ve Python’un izlediği yola benzer. Ama teknik ayrıntılarda bü- yük ayrılıklar vardır.
17.3 Module
Ruby’nin yukarıda açıkalanan probleme çözümü module yapısı ile verildi.
Modül (module) sınıflar, metotlar ve sabitler içeren bir birimdir. Ta- nımı sınıf tanımına benzer. Aslında her sınıf bir module’dir. Modül içinde anlık metotlar (instance methods) ve module metotları olabilir. Anlık me- totlar, sınıf içindeki anlık metotlar gibi davranırlar. Onlara ancak bir nesne içinden erişilebilir. Dolayısıyla modülün bir nesne içine konulması gerekir.
mixin denilen bu eylemi biraz sonra ele alacağız. Oysa module metotlarını çağırmak için modülü içeren bir nesne üretilmesi gerekmez.
Modüllerin iki önemli yararı vardır:
1. Her modülün kendi aduzayı vardır. Aduzaylarını bilgisayarımızın ka- yıt ortamında yarattığımız dizinlere benzetebiliriz. Modüller, büyük programlarda aynı adlı nesnelerin çakışmasını önler. Örneğin aynı adı alan iki metot farklı modüller içine konulursa, her biri kendi modülü içindeki rolünü oynar; sonra yazılan öncekinin yerini almaz. Bir modül içindeki öğelere (sabit, metot, sınıf) ancak o modül içinden erişilebilir.
[Bir Dizin içindeki dosyalar imiş gibi düşününüz]
2. Modüller başka sınıfların nesneleri içine konulabilir; yani modüller
17.3. MODULE 217 mixin uygulamasına izin verirler (Bunu biraz sonra açıklayacağımızı söylemiştik).
Her metot bir modüldür.
Her sınıf bir modüldür.
Modüller sabitler ve metotlar içerir.
Modüller aduzayı olarak kullanılır.
Modül bildirimi için sözdizimi şöyledir:
Liste 17.1.
m od ul e Ad deyim1 deyim2
. . . .
5 end
17.3.1 Module Tanımı Liste 17.2.
m od ul e Kurum d e f selam
p u t s " Merhaba "
end
5 end
Modül sabitleri sınıf sabitleri gibidir; adları büyük harfle başlar. Mo- dül metotları da sınıf metotları gibi tanımlanır. Ama modüller iki tür metot içerebilir: Anlık metot, module metodu.
Program 17.1.
m od ul e RuhHali
IYIMSER = " i y i m s e r i m "
KOTUMSER = " kötümserim "
5 d e f selam
r e t u r n " Ben #{IYIMSER } . Sen n a s ı l s ı n ? "
end
d e f RuhHali . selam
10 r e t u r n " Ben #{KOTUMSER} . Sen n a s ı l s ı n ? "
end end
15 p u t s (" S a b i t ça ğ ı r ma : RuhHali : : IYIMSER") p u t s ( RuhHali : : IYIMSER)
p u t s ( " Module Metodu ça ğ ı r ma : RuhHali . g r e e t " ) p u t s ( RuhHali . selam )
Açıklamalar:
• Program 17.1, RuhHali adlı bir modül tanımlıyor.
• Bu modül IYIMSER ve KOTUMSER adlı iki sabit ile selam adlı anlık bir metot ve RuhHali.selam adlı bir module metodu içeriyor. Öntakı- sız yazılan selam metodu anlık (instance) bir metottur. Modül adını öntakı olarak alan RuhHali.selam metodu ise bir module metodudur.
• Bu modülü bir sınıfa dönüştürmek için ilk satırındaki module yerine class yazmak yeterlidir.
• Tanımları benzer olmasına karşılık, sınıf ile module arasında önemli bir fark vardır: Sınıflardan nesne üretilebilir. Alt sınıfları tanımlanabi- lir. Üst sınıftaki özelikler oğullara kalıtsal olarak geçer (inheritance).
Oysa, modüllerin nesnesi ve alt modülleri olmaz.
Son söylediklerimize bakarak, modüllerin ne işe yaradığı sorusu akla gelebilir. Sınıf gibi tanımlanıyor ama sınıfın nitelikleine bile sahip değilse, neden modül ile uğraşıyoruz?
Bu sorunun yanıtını, aslında bu bölümün girişinde verdik.
Ruby’nin Class sınıfının önemli modülleri vardır. Math modülüne he- pimiz aşinayız. Math modülünün sabitlerini ve metotlarını izinsiz kulanırız.
Örneğin, PI sabiti, sqrt metodu, trigonometrik ve loharitmik fonksiyonlar bunlardandır. Ruby’de Class sınıfının verdiği ikinci önemli modül Kernel adını alır. Kernel modülü şimdiye dek kullandığımız print, puts, gets gibi metotları bize sunan modüldür. Bunların metotlarını listelemek için met- hods metodunu kullanabilirsiniz:
Kernel.methods
irb etkileşimli kabuğunda yazacağınız bu komut size Kernel modülünün bü- tün metotlarını verecektir.
Sabitler
Aslında sabitler birer değişkendir; tek farkları değerlerinin değişmiyor olu- şudur. Çok istiyorsanız sabitin değerini elbette değiştirebilirsiniz; ama o zaman Ruby sizi uyaracaktır.
17.3. MODULE 219 Modül’den Sabit Çağırma
Modül’den sabit çağıran sözdizimi örneği: aaa modül, ccc çağrılacak sabit ise
aaa::ccc
yazılır. Modül_adından sonra ard arda iki tane üst üste iki nokta (::) yı izleyen sabit_adı.
Örnek 17.1’de RuhHali::IYIMSER
deyimi, modülden IYIMSER adlı sabiti çağırır.
Modül’den Metot Çağırma
Modül’den module metodu çağıran sözdizimi şöyledir: aaa modül, mmm çağ- rılacak metot ise,
aaa.mmm
yazılır. Modül_adı, nokta bağlacı (.), metot_adı. Örnek 17.1’deki RuhHali.selam modül metodunu çağırmak için
RuhHali.selam deyimi yeterlidir.
Ama, anlık metot için, puts (selam) deyimi iş yapmayacaktır. O metodu ancak RuhHali modülünü içeren bir nesne içinden çağırabiliriz.
include
Bir sınıftan üretilen nesne içine bir modülü çağırmak için include (module_adı)
deyimi yeterlidir. Bu deyimden sonra modül içindeki her şey nesne içine girecektir. Örnek 17.1’deki selam anlık metodunu çağırmak için
puts (selam) deyimi yeterlidir.
17.4 mixin
Aslında, yukarıda yaptıklarımız mixin işlemini gerçekleştirmiştir. Özetler- sek, include metodu ile bir modülü bir nesne içine koymuş oluruz. O andan itibaren, modülün bütün öğeleri nesnenin olur; onlara nesne içinden adla- rıyla erişilebilir.
Program 17.1’ye puts (selam)
deyimini ekleyiniz. irb’nin
1 u n d e f i n e d l o c a l v a r i a b l e o r method ‘ selam’ f o r main : O b j e c t ( NameError )
hata uyarısını verdiğini göreceksiniz. Bunun nedeni, modül içindeki anlık metoda doğrudan erişmek istememizdir. Anlık metotlara ancak bir nesne içinden erişilebilir. Öyleyse, önce bir sınıf tanımlayalım, o sınıfın bir nesnesi içine include metodu ile modülü çağıralım. Ondan sonra modülün selam adlı anlık modülüne erişebiliriz.
17.5 Aduzayları 17.6 index
Aduzayı kavramı başka dillerde de kullanılır. Birbirleriyle ilgili program öğelerinin adlarını mantıksal (logic) olarak bir araya getiren bir yapıdır.
Ruby’de her module bir aduzayı belirler. Module öğeleri kendi aralarında iletişim kurabilirler; ama yapı dışarıya kapalıdır. Sanki çevresi kalın du- varlarla çevrilidir. Dışarıdan modülün öğelerine erişim için özel yöntemler gerekir. Onları biraz sonra ele alacağız. Örnek 17.1 ile 17.2 ile tanımlanan Daire ve Kare modüllerinin içerdiği anlık alan_bul metodu ile cevre_bul module metotlarının adları aynıdır. Ama bunlar farklı aduzaylarında yer aldığı için çakışmazlar. Öncekiler dairenin alanı ve çevresini bulurken, son- rakiler karenin alan ve çevresini bulur.
Aşağıda iki modül bildirimi yapılmıştır. Her iki modülde yer alan aynı adlı metotlar, birbirleriyle çakışmaz, kendi modülleri içinde varlıklarını ve işlevlerini sürdürürler
Örnek 17.1.
#! / u s r / b i n / ruby
17.6. INDEX 221
m od ul e D a i r e
4 PI = 3 . 1 4 1 5 9 2 6 5 4 R = 3
d e f alan_bul a l a n = PI ∗ (R∗∗2) end
9
d e f D a i r e . c e v r e _ b u l c e v r e = 2∗ PI∗R end
end
14
p u t s (" D a i r e n i n y a r ı ç a p ı n ı ç a ğ ı r : D a i r e : : R") p u t s ( D a i r e : : R)
p u t s (" D a i r e n i n ç e v r e s i n i bulan module metodunu ç a ğ ı r : D a i r e . c e v r e \ _bul ")
19 p u t s D a i r e . c e v r e _ b u l
1 /∗
3
1 8 . 8 4 9 5 5 5 9 2 4
∗/
Örnek 17.2.
1 #! / u s r / b i n / ruby
m od ul e Kare KENAR = 3
6 d e f alan_bul
a l a n = KENAR∗KENAR end
d e f Kare . c e v r e _ b u l
11 c e v r e = 4∗KENAR end
end
p u t s (" Karenin b i r k e n a r ı : Kare : : KENAR")
16 p u t s ( Kare : : KENAR)
p u t s (" Karenin ç e v r e s i n i bulan module metodunu ç a ğ ı r : Kare . c e v r e \ _bul ")
p u t s Kare . c e v r e _ b u l
1 /∗
3 12
∗/
Module Çağırma : include
17.7 include
Bir modülü bir sınıf içine çağırmak için include anahtar sözcüğünü kulla- nırız. Çağrılan modülün her şeyi sınıfın öğesi olur.
Liste 17.3.
1 c l a s s P e r s o n e l i n c l u d e Kurum end
selma = P e r s o n e l . new
6 selma . selam
17.7.1 Modül’den Nesne Üretilemez
Program 17.2 ile tanımlanan Kurum modülünden new operatörü ile bir nesne yaratmayı deneyelim. Derleyici aşağıdaki uyarıyı vererektir. uyarı, modulün new metodunun olmadığını; dolayısıyla onun bir nesnesinin yara- tılamayacağını söylüyor.
Kurum.new #=> undefined method ’new’ for Kurum:Module
17.8 Sınıf ile Modül Karşılaştırması 17.9 Çağrı Yöntemleri
load, require, include, extend
17.10 load 17.11 require 17.12 include 17.13 extend
Hepsi aynı işleve sahipmiş gibi görünüyor.
17.13. EXTEND 223
Tablo 17.1: Sınıf ile Module Karşılaştırması
sınıf (class) module
üretim (ins-
tantiation) üretilebilir üretilemez
kullanım nesne yaratılır mixin, aduzayı
üst sınıf module object
öğeleri metotlar, sabitler, değişkenler metotlar, sabitler, sınıflar metotlar sınıf metodu, anlık metot module metodu, anlık metot kalıtım eylemleri kalıtsal alır/verir kalıtım yok
içerme içerilemez sınıfta ve başka modülde inc-
lude metodu ile içerilebilir genişleme extend metodu ile genişleye-
mez; kalıtım ile genişleyebilir
üretilen modül genişleyebilir:
’singleton’
load : Bir dosya içeriğini yükler.
load ’deneme.rb’
Aynı dosya birden çok kez yüklenebilir (load). Sonuncu etkin olur.
require : Bir dosyadan seçilenleri alır require’deneme’
Birden çok kez seçim yapılabilir. İlk yapılan seçim etkindir.
include : Modülü, çağıran sınıftan üretilen nesne içine alır ve nesnenin öğeleri gibi kullanılmasını sağlar.
include deneme
extend : Sınıfın metotlarını ekleyerek onu çağıran modülü genişletir.
extend deneme
• Sınıf, modüldeki metoda sahipse, modül sonradan çağrılmış olsa bile sınıfın metodu etkin olur. Başka bir deyişle, sınıf daha baskındır.
• Çağrılan iki modülde aynı adlı metot varsa, son çağrılandaki etkin olur, önce çağrılan modüldeki ihmal edilir.
• Bir modül iki kez çağrılırsa, ilk çağrı etkin olur, sonraki çağrı yok sayılır.
17.14 Uygulamalar
Liste 17.4.
c l a s s Soyut i n c l u d e A i n c l u d e B
4 i n c l u d e A # => e t k i s i z d i r ; i l k ç a ğ r ı g e ç e r l i d i r end
Liste 17.5.
mo d ul e A d e f a1
p u t s ’ a1 ç a ğ r ı l d ı ’ end
5 end
mo d ul e B d e f b1
p u t s ’ b1 ç a ğ r ı l d ı ’
10 end end
mo d ul e C d e f c1
15 p u t s ’ c1 ç a ğ r ı l d ı ’ end
end
c l a s s Test
20 i n c l u d e A i n c l u d e B i n c l u d e C
d e f d i s p l a y
25 p u t s " M o d ü l l e r ’ i n c l u d e d ’ i l e ç a ğ r ı l d ı "
end end
o b j e c t=Test . new
30 o b j e c t . d i s p l a y o b j e c t . a1 o b j e c t . b1 o b j e c t . c1
Array sınıfının local_variables metodu, yerel değişkenleri listeler:
Liste 17.6.
# e n c o d i n g UTF−8
2 mo d ul e ModuleM m1 , m2 = 4
17.14. UYGULAMALAR 225
p u t s " module i ç i n d e "
p u t s l o c a l _ v a r i a b l e s
7 end
d e f method1 v , w = 3
12 p u t s " method i ç i n d e "
p u t s l o c a l _ v a r i a b l e s end
17 c l a s s Some x , y = 2
p u t s " c l a s s i ç i n d e "
p u t s l o c a l _ v a r i a b l e s end
22
method1 t1 , t 2 = 7
27 p u t s " I n s i d e t o p l e v e l "
p u t s l o c a l _ v a r i a b l e s
Program 17.2.
m od ul e Meyveler
2 @@meyve = ’ k i r a z ’
d e f s e l f. s e t ( x )
@@meyve = x end
7
d e f s e l f. g e t
@@meyve end end
12
p T . g e t #=> ’ k i r a z ’ T . s e t (’ elma ’)
p T : : g e t #=> ’ elma
Program 17.3, bir module içindeki anlık değişkene bir sınıf içinden erişimi gösteriyor.
Program 17.3.
m od ul e Kimlik
a t t r _ a c c e s s o r : kimlik_no d e f i n i t i a l i z e
5 @kimlik_no = 123
end end
c l a s s P e r s o n e l
10 i n c l u d e Kimlik d e f i n i t i a l i z e
s u p e r end
d e f kimlik_no_yaz
15 p u t s @kimlik_no end
end
m = P e r s o n e l . new
20m. kimlik_no_yaz
#=> 123
Program 17.4.
# e n c o d i n g UTF−8
3
mo d ul e K e d i l e r s a y = 2 c l a s s Kedi
d e f s e s _ v e r
8 p u t s " Miyauuu . . . "
end
d e f yemek p u t s " . . . "
13 end
d e f kuyruk_oynat
p u t s " s i n i r l e n i n c e kuyruğumu o y n a t ı r ı m ! "
end
18
d e f pençe_at
p u t s " Olmaz , c a n ı n yanar ! "
end end
23 end
x = 5
t e k i r = K e d i l e r : : Kedi . new
t e k i r . kuyruk_oynat i f x >= K e d i l e r : : sa y
Program 17.5.
#e n c o d i n g : u t f −8
3 mo d ul e Hafta
ILK_GUN = " Pazar "
d e f Hafta . ayda_hafta
p u t s " B i r ayda d ö r t h a f t a v a r "
end
17.14. UYGULAMALAR 227
8 d e f Hafta . s e n e d e _ h a f t a
p u t s " B i r y ı l d a 52 h a f t a v a r "
end end
13
c l a s s On_sene i n c l u d e Hafta
s e n e _ s a y i s i =10
18 d e f a y _ s a y i s i
p u t s Hafta : : ILK_GUN s a y i =10∗12
p u t s s a y i end
23 end
d1=On_sene . new p u t s Hafta : : ILK_GUN Hafta . ayda_hafta Hafta . s e n e d e _ h a f t a
28 d1 . a y _ s a y i s i