Nesne tabanlı programlama, geliştirilen programlar büyüdükçe ortaya çıkan karmaşıklık sorununu çözmek için geliştirilmiş bir programlama yaklaşımıdır.
Çünkü büyük projeler üzerinde birden fazla programcının farklı kısımlarını kodladığı işlerdir ve hem başlangıçta hem de yeni versiyonlarının geliştirilip, mevcut olan kısımların bakımının yapılacağı ilerleyen süreçte proje üzerinde farklı programcılar çalışırken ortak bir dili ortak bir yapıyı kullanması önemlidir. Yoksa proje üzerinde çalışan her programcının farklı yapılar inşa etmesi, hem karmaşıklığı artırır, hem de projenin farklı bölümlerinin birbirleri arasında iletişimini zorlaştırır. Bu nedenle özellikle büyük projelerde ortak bir altyapıya sahip olmak önemlidir.
Sınıflar adı verilen bir yapı ile kullanılacak ortak özellikler(değişkenler) ve fonksiyonlar tanımlanır. Böylece kodlama yaparken kullanılacak olan veri yapısı ve işlevler sağlanmış olur. Artık bu sınıftan örnek bir nesne oluşturup o nesneyi ve o nesneye ait verileri kullanarak ilgili bölümün kodlaması yapılabilir.
Örneğin email gönderimi için; alıcı listesi, konu, ileti mesajı gibi özellikleri ve dosya ekleme, gönderme gibi fonksiyonları bir sınıf olarak tanımladığınızda, proje üzerinde çalışan her programcı bu sınıfın bir nesnesini oluşturup gerekli verileri sağlayarak kendi kodladığı bölümde email gönderilmesini sağlayabilir. Böylece projenin farklı bölümlerinde farklı yapılarda email gönderimi yapılarının oluşturacağı karmaşıklıktan ve kod kalabalığından kurtulmuş, daha düzenli ve performanslı uygulamalar elde etmiş oluruz.
Nesne tabanlı programlamanın temelinde, yeniden kullanılabilir kod yazmak vardır. Verimli olduğu kadar anlaşılması ve yönetilmesi kolay kodlama yapılmasını sağlar.
Sınıflar paylaşılabilir bir yapıya sahip olduğu için, aynı sınıfı farklı projelerde de kullanmanız mümkündür. Bunu yaparken veriler sınıf içinde bulunmadığından, yani nesne tabanlı programlama veri soyutlama mantığı ile çalıştığı için projeler birbirinden bağımsız kendi verileriyle çalışırlar. Bu durum sınıfları modüller gibi farklı projelerde kullanmanızı sağlar.
Nesne Tabanlı Programlama, başta Sınıf(Class) ve Nesne(Object) olmak üzere pek çok kavrama sahiptir;
- Yapıcı Metodlar(Constructors)
- Yıkıcı Metodlar(Destructures)
- Miras Alma(Inheritance)
- Kompozisyon(Composition)
- Kapsülleme(Encapsulation)
- Soyutlama(Abstraction)
- Çok Biçimlilik(Polymorphism)
- Operatörün Aşırı Yüklenmesi(Operator Overloading)
- Fonksiyonun Aşırı Yüklenmesi(Function Overloading)
Tüm bu kavramları, daha anlaşılır olmaları için yeri geldiğinde örnekleyerek açıklayacağız. Ancak bunun için örneklerle çalışmak üzere programlama kısmına geçmeliyiz.
Ama programlamadan önce bazı kavramların ne ifade ettiğini kısaca özetleyebiliriz. Tam olarak anlayabilmek için programlama kısımlarında tekrar değindiğimizde örneklerle daha anlaşılır ve elle tutulur hale gelecektir. O nedenle tanımlar ve açıklamalar soyut kalırsa endişelenmeyin.
Kapsülleme(Encapsulation)
Başlangıçta Kapsülleme anlamsız bir kelime gibi gelebilir ama bu kavrama bu adın verilmesinin nedeni
gizlemek istediğiniz şeyleri bir kapsülün içine koyarak saklamaktan gelmektedir. Tıpkı toz ilaçların bir arada tutulmak ve gizlenmek için renkli kapsüller içine konulması gibi düşünebilirsiniz.
Bu kavram, sınıflara ait özellikleri(değişkenleri) ve bu özellikler üzerinde çalışılmasını sağlayan metodları tek bir birim olarak bir arada tutan bir mekanizmadır. Ana amacı, özellikler ve dolayısıyla veriler üzerinde erişim kontrolü sağlamaktır. Kapsüllemede sınıf özellikleri(değişkenleri) diğer sınıflardan gizlenebilir ve sadece mevcut sınıfın metodları ile kullanılabilir. Bu nedenle, özellikleri gizlediği için veri saklama olarak da bilinir.
Bir başka deyişle; sınıf içindeki özellik ve metodlarınızı, sınıf dışında tanımlanmış diğer kodların rastgele erişip kullanmasından koruyan bir bariyer gibidir.
Büyük projeler ekip çalışması gerektirir. Ancak, ekip arkadaşlarınızın yazdığınız sınıflar ile çalışırken kodlarınızın bazı kısımlarında değiştirilmesini ya da ulaşılmasını istemediğiniz kısımlar olabilir. Bu durumda, erişim verip vermeyeceğinizi bir takım anahtar kelimeleri kullanarak tanımlayablirsiniz. Yani erişim yetkilendirmesini siz belirlersiniz. Bir çok programlama dilinde genellikle private(özel-gizli) ve public(herkese açık) kelimeleri erişim yetkilerini belirleme konusunda size yardımcı olurlar.
Diğer programcılar, bazı özel metodları çalıştırmak suretiyle sınıfın gizlenmiş özellik ve metodlarının sınırlandırılmış bir bölümünü kullanabilirler.
Kısaca özetlemek gerekirse, sınıflarınızı oluştururken diğer sınıfların erişmesini istemediğiniz özellik ve metodları private kelimesiyle korumaya alabilirsiniz. Python’da ise private kelimesi yerine kullanılabilecek simgeler vardır. Bunu ayrıca, Python’da Nesne Tabanlı Programlama bölümünde ele alacağız.
Soyutlama(Abstration)
Soyutlama, uygulamanın detaylarının kullanıcıdan gizlenmesi kavramıdır. Kullanıcının uygulamanın yazılış amacını yerine getirmesi ile ilgili beklentileri vardır, uygulamanın nasıl yazıldığı kullanıcıyı pek ilgilendirmez. Uygulamanın vadettiği işlemi yapıp yapamadığına bakar. Doğrusu da budur. Ama detaylar ile ilgili kullanıcıyı bilgilendirme konusunda sorumluluk yine size aittir.
Tıpkı arabanızın deposunda benzin olup olmadığını deponun kapağını açıp içine baktığında görememeniz gibi. Ya da benzin depolarının saydam ve dışarıdan görünür şekilde yapılmaması gibi. Siz bilginin asıl sağlandığı konumdan bağımsız bir şekilde size sunulan arayüz ile arabanızın kontrol paneline bakıp depoda ne kadar benzin kaldığını görebilirsiniz.
Uygulamanız içindeki karmaşık adımları, detayları, tanımlamaları, hesaplamaları, kontrol ifadelerini sınıfın içinde gizleyerek, bunlara erişebilecek herkese açık bir kapı olan metodları eklersiniz. Örneğin; Sarmalayan Sınıflar(Wrapper Classes) gibi.
Aslında biz de uygulamalarımızı yazarken benzer şekilde davranırız. Örneğin harici bir modülü uygulamanıza eklediğinizde onun hangi fonksiyonu yerine getirmesini istiyorsanız onu beklersiniz. Sadece sizin istediğiniz işlemi nasıl yerine getirdiği, yani nasıl kullanıldığı ile ilgili dokümanları okur ve o modülü kullanmayı öğrenirsiniz. Modülü yazan programcının o modülü nasıl yazdığı ile ilgilenmezsiniz. Modül kodlarını açıp nasıl yapıyormuş diye bakmazsınız. Yani nasıl yaptığı ile değil, nasıl kullanıldığı ile ilgilenirsiniz. İşte asıl kodlarda yer alan metodlar biz kullanıcılardan soyutlanmış ve bize sağlanan metodların kullanımı tarafımıza sunulmuştur.
Soyutlama kavramı, bir ya da birden fazla uygulamaya sahip olabilen bir kavramdır.
Örneğin siz bir arabaya baktığınızda gördüğünüz şey ile bir makina mühendisinin o arabaya baktığında gördüğü aynı olmayacaktır. Siz estetik hatlarını görüp beğenirken, hatta daha da meraklıysanız en çok motor gücüne, torkuna v.s bakarken, bir makina mühendisi motorun pistonlarının tasarımına, kullandıkları dişlilerin malzemesine hayran olacaktır.
Yani kullanacak kişilere göre araba sınıfına farklı özellikler koyabilirsiniz. Böylece aynı sınıf birden fazla uygulamaya da sahip olabilir.
Örneğin, bilgisayarınızda bir web sayfasını ziyaret etmek istediğinizi varsayalım. Bunun için bir web tarayıcı kullanırsınız. Web sayfasını görüntülemek için gerekli internet bağlantısını kurmanın da bir çok yolu vardır; kablosuz ağ ya da kablolu ağa bağlı olabilirsiniz. Web tarayıcınız için bunun bir önemi yoktur, o görevini yerine getirmek için bağlantının var olup olmaması ile ilgilenir. Ne şekilde bağlandığınız web tarayıcınızı ilgilendirmez. Çünkü internet bağlantısını gerçekleştiren yazılım web tarayıcınıza bir soyutlama sağlamıştır. Yani internete bağlantısı soyutlama(abstraction), kablolu ya da kablosuz bağlanma şekilleri bu soyutlamanın farklı uygulamaları(implementation) olur.
Soyutlama kavramı esasında yazılım tasarımı ve mimarisine ait bir kavramdır, sadece nesne tabanlı programlama ile ilgili bir kavram değildir.
Soyutlama, büyük ve kompleks sistemler geliştirirken, karmaşık işlemleri seviye seviye küçük parçalara ayırıp sistemi adım adım geliştirmeye yardım eder. Böylece birlikte çalışan ama birbirinin neyi nasıl yaptığına karışmayan büyük sistemler oluşturabiliriz. Bu sayede, sistem çalışmaya başladıktan sonra bakım aşamasında da; sistemin bir parçasının geliştirilmesi ve yeni özellikler eklenmesi gerektiğinde sadece o parça ile ilgilenmek yeterli olur. Sistemin tümünü ele almak yerine ilgili parçaya bakım uygulamak zamandan ve işgücünden kazandıracağı gibi, muhtemel hataları da azaltır.
Örnek olması bakımından yine araba ile ilgili söylediklerimizi hatırlarsanız; arabanın estetik, motor, şasi, yürüyen ve lastikler ile ilgili özelliklerini tek bir sınıf içinde tanımlamak yerine her birini kendine ait bir sınıf içinde tanımladığınızı hayal edin. Tek bir sınıf olursa, içinde yüzlerce belki binlerce özellik ve fonksiyon yer alacaktır. Ancak arabayı oluşturan mekanik, tasarım, motor ve mühendislik kısımlarını kendi sınıfları içinde tanımlarsanız, bir sorunla karşılaştığınızda hangi sınıftan kaynaklandığını bulmanız ve çözmeniz daha kolay olacaktır. Her zaman basitten komplekse doğru ilerlemekte fayda vardır. Yani basit ve küçük parçaların birleşerek oluşturduğu büyük ve kompleks bir uygulama doğru olandır, tıpkı tek bir civata gibi binlerce parçadan, kompleks bir araca dönüşen arabalar gibi.
Soyutlama, ekip çalışmalarında işlerinizi kolaylaştırır. Geliştireceğiniz büyük uygulamayı hazırlarken, ekibinizdeki kişiler farklı bölümlerinden sorumlu olurlar. Önce uygulamada temel alınacak soyut bir sınıf oluşturur, içine özellikleri ve ekipteki diğer yazılımcıların oluşturması gereken metodları pass deyimiyle eklersiniz. Böylece bu sınıftan miras alarak kendi bölümlerini yazmaya başlayan diğer yazılımcıların elinde temel özellikler ve yazacakları metodlar mevcut olur. Soyut bir sınıf oluşturduğumuzda bu soyut sınıfı miras alan alt sınıfların her biri soyut sınıfa ait olan metotları kullanmak zorundadır. Tıpkı mimarın çizimini eline alıp evi inşa eden mühendisler gibi. Şablona bağlı kalarak uygulamayı hayata geçirirler. İşte burada şablon oluşturma işlemi Soyutlama’dır.
Soyutlama(Abstraction), Kompozisyon(Composition) ile de sağlanabilir.
Soyutlama, genel bir yazılım mimarisi yaklaşımı olmakla birlikte nesne tabanlı programlamanın da önemli bir bileşenidir. Karmaşıklığı azaltmaya ve sürdürülebilir kompleks sistemler oluşturmaya yardımcı olur. Kapsülleme(Encapsulation) ve Çok Biçimlilik(Polymorphism) ile birlikte kullanılarak nesne tabanlı programlamaya güç katar.
Miras(Inheritance)
Miras kavramı tıpkı isimlendirildiği şekilde, bir sınıfın diğer bir sınıfın sahip olduğu özellikler ve metodları alıp kullanabilmesi olarak açıklanabilir.
Miras kavramı ilişkilendirme temellidir. Bu ilişkilendirmeye bağlı olarak farklı miras alma tipleri mevcuttur;
- Tek(Single) Miras: Sadece babanızdan kalan miras gibi düşünebilirsiniz.
- Çoklu(Multiple) Miras: Babanızdan ve annenizden ayrı ayrı miras kaldığını düşünebilirsiniz.
- Çok Seviyeli(Multi-Level) Miras: Dedenizden anne-babanıza kalan mirasın size kalması gibi düşünebilirsiniz.
- Hiyerarşik(Hierarchical) Miras: Babanızdan kalan mirasın kardeşinizle aranızda bölüşülmesi gibi düşünebilirsiniz.
- Karışım(Hybrid) Miras: Yukarıdaki seçeneklerin bir karışımıdır. Örneğin; Babanızın yeni eşinden olan üvey kardeşleriniz hem babanızın hem de dedenizden annenize kalan mirası paylaşırken, sizin sadece babanızdan kalan mirasa sahip olmanız gibi.
Java ve Javascript gibi bir çok programlama dili, çoklu mirasa sahip değildir. Çoklu miras olmadan da karışım mirasından söz etmek mümkün değildir. Bu durum Elmas Problemi(Diamond Problem) adı verilen bir durumdan kaynaklanmaktadır. Bu diller ile çalışanlar araştırıp neden kullanılamadığını inceleyebilsinler diye şimdilik sadece adını vermekle yetineceğim. Dile özgü bir durum sayılabileceği için detaya girmiyorum.
Miras kavramı kodun yeniden kullanılabilirliğini sağladığı için önemlidir. Böylece programlamanın temel prensiplerinden Kendini Tekrar Etme! DRY(Don’t Repeat Yourself) kavramına uygundur. Birden çok sınıf içinde kullanmanız gereken özellik ve metodları tekrar tekrar yazmanıza gerek kalmaz, bir kez yazar diğer sınıfa miras bırakıp kullanılmasını sağlarsınız.
Yeniden kullanılabilirlik; kodun bakımını kolaylaştırır, işgücünden ve zamandan kazandırırken maliyetleri de düşürür.
Ama bu avantajlarının yanısıra bazı dezavantajları da vardır. Miras alınan fonksiyonlar dolaylı yoldan çalıştırıldığı için sınıfın kendi fonksiyonlarından biraz daha yavaş çalışır. Genellikle miras alınan sınıfın verileri kullanılmadan bırakılır ve bu da gereksiz bellek kullanımı doğurabilir. Miras alınan sınıfta bir değişiklik yapacağınız zaman ondan miras alan tüm alt sınıflarında etkileneceğini gözden kaçırırsanız alt sınıflarda hatalara yol açabilirsiniz.
Miras kavramı profesyonelce planlanarak kullanıldığında çok faydalıdır. Ancak yeni bir programcıysanız ve gerçekten ihtiyacınız olduğundan emin olamıyorsanız miras kullanma konusunda seçici davranarak olumsuz sonuçlardan kaçınabilirsiniz.
Çok Biçimlilik(Polimorphism)
Çok biçimlilik; bir kavrama farklı alanlarda, farklı kullanım amacı ya da anlamı vermektir. Yani kavram tektir, ama farklı bakış açılarından ele alındığında farklı anlama gelir. Bu kapsamda değerlendirildiğinde dilbilgisindeki eş anlamlılara benzer. Örneğin, kesmek diye ifade ettiğimiz şey; berber için saçı makasla kısaltmak işlevini, aktör için ise sahneyi bitirme komutunu ifade eder.
Programlamada 2 tür çok biçimlilik uygulaması vardır;
- Statik Çok Biçimlilik
- Dinamik Çok Biçimlilik
Çok biçimliliğin özellikleri genellikle programlama diline bağlıdır. Örneğin; Java ve C++ dillerinde Fonksiyonların Aşırı Yüklenmesi(Function Overloading) mebvcut iken, Javascript’te bu özellik yoktur. Operatörlerin Aşırı Yüklenmesi(Operator Overloading) ise yalnızca C++’da mevcuttur. Bu nedenle kullandığınız programlama diline özgü özelliklerini öğrenmek için dilin kendi dokümantasyonunda Çok Biçimliliğin hangi özelliklere sahip olduğuna göz atmanız da fayda vardır.
Yapıcı Metodlar(Constructors)
Aslında sözlük çevirisi her ne kadar Yapıcı Metodlar(Constructor) olarak ifade edilse de ben şahsen yaptığı iş bakımından anlamını değerlendirerek bunlara Başlangıç Metodları demeyi daha çok seviyorum. Çünkü esas işleri, bir sınıf oluşturulduğunda özelliklerin ve temel işlevlerin tanımlanmasını sağlamaktır. Böylece bu sınıftan bir nesne oluşturduğunuzda, nesnenin sahip olması gereken başlangıç değer ve işlevlerini oluştururlar.
Yıkıcı Metodlar(Destructures)
Bu metodların da, sözlük çevirisi her ne kadar Yıkıcı Metodlar(Destructures) olarak ifade edilse de ben şahsen yaptığı iş bakımından anlamını değerlendirerek bunlara Kapanış Metodları demeyi daha çok seviyorum. Çünkü bu metodların da işlevleri, sınıfın sonunda gerçekleştirilen işlemlerin sonlandırılması için atanan değerlerin sıfırlanması gibi bazı sistemsel işlevleri yerine getirmek için kullanılırlar.
Bağlantıda Kalalım