10. Nesne Yönelimli Düşünme
Nesne yönelimlilik kavramı çekici bir kavramdır. Herşeyi nesneye yönelik olarak çağırmak kulağınıza hoş gelebilir. Ruby nesne yönelimli bir betik dili olarak adlandırılır, ancak gerçekte bu "nesne yönelimlilik" kavramı nedir?
Bu soruya aşağı yukarı hepsi aynı kapıya çıkan bir sürü cevap bulunabilir. Çabukça toparlamak yerine, isterseniz öncelikle geleneksel yazılım kavramı üzerinde duralım.
Geleneksel olarak, bir yazılım geliştirme sorunu bazı verilerin gösterimleri ile bu veriler üzerinde çalışan yordamlar olarak karşımıza çıkar. Bu model altında, veri hareketsiz, edilgen ve beceriksizdir; tamamen etkin, mantıksal ve güçlü bir yordamın merhametine kalmıştır.
Bu yaklaşımdaki sorun, yazılımları geliştiren yazılımcıların sadece insan olması ve dolayısıyla bir çok ayrıntıyı sadece bir sefer kafalarında net olarak tutabilmeleridir. Proje genişledikçe, yordamsal öz daha karmaşık ve hatırlaması zor bir noktaya gelir.
Küçük düşünce kusurları ve yazım yanlışlarıyla sonuçta elinizde iyi-gizlenmiş yazılım hataları kalır.
Zamanla yordam çekirdeğinde istenmeyen etkileşimler doğabilir; bu iş dokunaçlarının yüzünüze değmesine izin vermeden sinirli bir mürekkep balığı taşımaya benzer.
Bu geleneksel zorlamalarla yazılım geliştirirken hataları azaltmak ve sınırlamak için kılavuzlar bulunmaktadır, ancak yöntemi kökten değiştirmek daha iyi bir çözüm olacaktır.
Peki nesneye yönelik yazılım geliştirme, mantıksal işin sıradan ve tekrarlayan yönünü verinin kendisine emanet etmemizi mümkün kılmak ve veriyi edilgen durumdan etkin duruma sokmamız için ne yapar? Başka bir açıdan,
-
Her veri parçasına, erişip içindekileri etrafa fırlatmamıza izin veren kapağı açık bir kutu gibi davranmayı bıraktık.
-
Her veri parçasına kapağı kapalı ve iyi işaretlenmiş düğmeleri bulunan çalışan bir makine gibi davranmaya başladık.
"Makine" olarak tanımladığımız şey çok basit ya da çok karmaşık olabilir ancak bunu dışarıdan bakarak söyleyemeyiz ve makineyi açmayı (tasarımıyla ilgili bir sorun olduğunu düşünmedikçe) istemeyiz. Bu yüzden veriyle etkileşimde bulunmak için düğme çeviriyor gibi işlem yapmamız gerekir. Makine bir kere kurulduğu zaman nasıl çalıştığı hakkında düşünmemize gerek yoktur.
Kendimize iş çıkardığımızı düşünebilirsiniz ancak bu yaklaşımla bazı şeylerin yanlış gitmesini önleyebiliriz.
Şimdi açıklayıcı olması açısından basit ve küçük bir örnek görelim: Arabanızın bir yolmetresi olsun. Görevi yeniden başlatma düğmesine son basıldığından itibaren ne kadar yol katedildiği ölçmektir. Bu durumu bir yazılım geliştirme dilinde nasıl tasarlayabiliriz? C'de yolmetre sadece sayısal bir değişken olmalıdır, muthemelen bir float. Yazılım bu değişkenin değerini küçük aralıklarla arttıracak, uygun gördüğü zamansa sıfır yapıp yeniden başlatacaktır. Burada yanlış olan nedir? Yazılımdaki bir hata bu değişkene uydurma bir değer atayabilir ve beklenmedik sonuçlar ortaya çıkabilir. C'de yazılım geliştirmiş herhangi biri böylesine küçük ve basit bir hatayı bulmak için saatler ya da günler harcamanın ne demek olduğunu bilir (hatanın bulunma sinyali genelde alında şaklayan bir tokattır).
Aynı sorun nesneye yönelik bağlamda da karşımıza çıkabilirdi. Yolmetreyi tasarlayan bir yazılımcının soracağı ilk şeylerden biri tabii ki "hangi veri yapısı bu durum için daha uygundur?" olmayacaktır. Ama "Bunun tam olarak nasıl çalışması gerekiyor?" şeklinde bir soru daha uygun olacaktır. Aradaki fark daha malumatlı olmaktır. Bir kilometre sayacının gerçekte ne işe yaradığına ve dış dünyanın onunla nasıl etkileşimde bulunmayı beklediğine karar vermek için biraz zaman ayırmamız gereklidir. Şimdi arttırabileceğimiz, yeniden başlatabileceğimiz ve değerini okuyabileceğimiz ve başka bir şey yapmayan küçük bir makine yapmaya karar verdik.
Yolmetremize keyfi bir değer atamak için bir yol tanımlamadık; neden? Çünkü yolmetrelerin bu şekilde çalışmadığını biliyoruz. Yolmetreyle yapabileceğiniz pek az şey var, ki bunların hepsini yapmaya izin verdik. Bu şekilde eğer yazılımda herhangi birşey yolmetrenin değerinin yerine geçmeye çalışırsa (örneğin arabanın klimasının derecesi) işlerin yanlış gittiğine dair uyarı alırsınız. Koşan yazılımın (dilin doğasına göre muhtemelen derleme sırasında) yolmetre nesnelerine keyfi değerler atamaya izni olmadığını söyledik. Mesaj tam olarak bu olmayabilir ama buna yakın birşeydir. Ancak hatayı engellemiyor, değil mi? Ancak hatanın yerini kolayca gösterir. Bu nesneye yönelik yazılım geliştirmenin zamanımızı boşa harcamaktan kurtaran birkaç yolundan biridir.
Yukarıda soyutlamanın yalnızca bir adımını yaptık, artık makinelerden oluşan bir fabrika yapmak kolaylaştı. Tek bir yolmetreyi doğrudan oluşturmak yerine, basit bir kalıptan istediğimiz sayıda yolmetre yapmayı tercih etmeliyiz. Kalıp (ya da isterseniz yolmetre fabrikası) "sınıf" olarak adlandırdığımız kavrama, oluşturduğumuz yolmetre de "nesne" olarak tanımladığımız kavrama karşılık gelmektedir. Bir çok nesneye yönelik yazılım geliştirme dili, herhangi bir nesne oluşturmdan önce bir sınıfın tanımlı olmasını gerekli kılar, ancak Ruby'de böyle bir durum sözkonusu değildir.
Bu kullanımın nesneye yönelik tasarımı kuvvetlendirmediğini de bir kenara yazalım. Elbette her dilde, anlaşılamayan, hatalı, yarım yamalak kod yazmak mümkündür. Ruby'nin sizin için yaptığı şey (özellikle C++'nın aksine) nesneye yönelik yazılım geliştirme kavramını sindirmenizi sağlayarak, daha küçük bir ölçekte çalışırken çirkin bir kod yazmamak için çaba sarfetmenizi önler. İleriki bölümlerde Ruby'nin takdire şayan diğer özelliklerini açıklayacağız. Hala bizimle misiniz?