Kotlin ve Nesne Tabanlı Programlama (OOP)

Baysan
10 min readJul 15, 2020

--

Bu yazımda sizlere Kotlin ve Nesne Tabalı Programlama’yı anlatmaya çalışacağım.

Nesne Tabanlı Programlama Nedir?

Nesne Tabanlı Programlama mantıksal işlemlerden ziyade her işlevin bir nesne olarak soyutlandırıldığı programlama yaklaşımıdır. Bir nevi gerçek hayatta gördüğümüz nesneleri modelleyerek bilgisayar ortamında entity (varlık) haline getirme işlemi de diyebiliriz. Alt başlıklarda daha detaylı değineceğiz fakat yine de ufak bir örnek yapmakta sakınca yoktur diye düşünüyorum.

insan diye bir değişkenimiz olduğunu varsayalım ve hatırlayalım ne demiştik? ‘gerçek hayatta gördüğümüz nesneleri modelleyerek…’

Bir insan değişkeninin birden fazla özelliği olabilir. Bu durumda her özelliği ayrı ayrı değişkenler içerisinde saklamak ne kadar mantıklı olur?

Ya da bir ERP programı içerisinde bir sürü insan3 insan4 insan5 … değişkenleri program akışına dahil olsa insan değişkenlerini yönetmek ne kadar zor olurdu değil mi? İşte tam burada Nesne Tabanlı Programlama (OOP) imdadımıza yetişiyor ve bu sayede genel bir Insan sınıfı oluşturuyoruz ve bu sınıftan nesneler (object) üretebiliyoruz. Her nesnenin kendine ait insani özelliklerini, Insan sınıfından ürettiğimiz nesneye has olarak set edebiliyoruz. Insan sınıfından nesneler üretmemizi sağlayan yaklaşımın adı Nesne Tabanlı Programlama’dır.

Nesne Tabanlı Programlama yaklaşımında 4 temel özelliğin gerçekleştirilmesi zorunludur. Bu özelliklerden birini bile sağlamayan bir dil saf (pure) Nesne Tabanlı Programlama Dili sayılmamaktadır. Bu özellikler:

  • Soyutlama (Abstraction)
  • Paketleme (Encapsulation)
  • Miras Alma (Inheritance)
  • Çok Biçimlilik (Polymorphism)

Nesne Tabanlı Programlamanın Faydaları Nelerdir?

  • Yazdığımız kodun tekrar kullanılabilmesini (code-reusability) sağlar.
  • Yazdığımız bir sınıfa özellikler ve metodlar ekleyebildiğimizden bize Genişletilebilirlik (extensibility) sağlar.
  • Yordamsal Programlama zamanında yazılan kodların spagetti kod’a dönüşmesi biraz daha kolay olduğundan, OOP sayesinde projelerin bakımları kolaylaşmıştır.
  • Sürdürülebilirliği (maintainable) sağlamaktadır. Bu sayede kodlara bakım yapıldığı zaman bir çok fonksiyon üzerinde oynama yapmamıza gerek kalmadan az sayıda sınıf üzerinde güncelleme yaparak kodlarımızı güncelleyebiliriz.
  • Zamandan tasarruf (timely) etmemiz kolaylaşır. OOP sayesinde birden fazla kod parçası üzerinde birden fazla geliştirici rahatlıkla çalışabilir.

Nesne Tabanlı Programlamanın Temel Kavramları Nelerdir?

Sınıf (Class)

Nesne Tabanlı Programlamayı tanımlarken ne demiştik?gerçek hayatta gördüğümüz nesneleri modelleyerek…’ Bir User sınıfımız olduğunu varsayalım ve bu sınıfı bilgisayar ortamında modellemeye çalışalım.

Oluşturmak istediğimiz sınıfının önüne class ön ekini kullanmamız gerekmektedir. Ardından sınıfımızın adını yazmamız yeterli olacaktır.

User.kt

Nesne (Object)

Gerçek hayattan modellediğimiz sınıflardan ürettiğimiz varlıklara ise nesne (object) denmektedir.

MainActivity.kt

Özellik (Property)

Başarılı bir şekilde User sınıfımızı ve myUser nesnemizi oluşturduk. Peki her User’ın bir veya birden fazla özelliği yok mudur? İsim, yaş, maaş vb. Bunun gibi User sınıfına ait olan özelliklere ise property denmektedir. Aşağıdaki örnekte User sınıfına ait name ve age property’leri tanımlanmıştır.

User.kt

Diğer dillerden pek alışık olmadığımız bir yapı dikkatimizi çekiyor burada. String? = null

Kotlin’de bir property tanımladığımız zaman bu property’i initialize etmemiz yani değerini direkt set etmemiz, sınıftan oluşturulan bir nesnenin ilgili özelliğinin default değerini belirtmemiz gerekmektedir. Bu durumda oluşturulan nesnenin default olarak name özelliği “ ” (boş string) ve age özelliği 0 olacaktır.

User.kt

Eğer özelliğin değerini daha sonradan belirlemek istersek ilk örnekteki gibi default olarak null set ederiz. Bir değeri null olarak set etmemizi sağlayan operatör ise ? operatörüdür.

User.kt

Bir nesnenin özelliklerine ve metodlarına erişmek istersek ‘.’ operatörünü kullanmamız gerekmektedir -> objectName.propertyName

MainActivity.kt

Metod (Method)

Sınıflara özel olarak tanımladığımız fonksiyonlara metod denmektedir. Yukarıda yazdığımız User modeline selamla adında bir metod ekleyelim ve hatırlayalım. “Yazdığımız bir sınıfa özellikler ve metodlar ekleyebildiğimizden bize Genişletilebilirlik (extensibility) sağlar.”

User.kt
MainActivity.kt

This Anahtar Sözcüğü

This anahtar sözcüğü diğer dillerde olduğu gibi Kotlin’de de bir referans değişkenidir. Mevcut nesneyi temsil eder. Sınıf içerisinde kullanıldığında da sınıfın kendisini temsil eder.

Aşağıdaki örnekte User sınıfına kendiniTanit adında bir metod tanımlanmıştır. Bu metod sınıftan üretilen nesnenin kendisine ait olan name özelliğini kullanarak ilgili metodu çalıştırmaktadır. Buradaki this anahtar sözcüğü sınıfı temsil etmektedir.

Constructor (Yapıcı Metod)

Bir sınıftan bir nesne üretildiğinde çalışan ilk metoddur. Genelde nesne ilk üretildiğinde yapılmasını istediğimiz işlemleri bu metod içerisine yazarız. Örnek olarak nesne ilk oluşturulduğunda ilgili özelliklerini set etmek isteyebiliriz. Bu durumda Constructor metodlar çok işimize yaramaktadır.

Aşağıdaki örnekte User sınıfının constructor metodunu yazdık. Bu durumda User sınıfından bir nesne üretileceği zaman zorunlu olarak _name ve _age parametreleri verilecek ve verilen parametrelerde this anahtar sözcüğü yardımıyla User sınıfının özelliklerine (property) eşitlenecek. Bu örnek için myUser nesnesinin name özelliği ‘Baysan’ ve age özelliği 20 olacaktır. Çünkü en alt satırda daha sınıftan bir nesne oluşturulurken biz ilgili nesnenin özelliklerini belirledik (constructor).

Primary Constructor

Kotlin’de primary constructor adında diğer dillerden bildiğimiz alışılagelmişten biraz daha farklı bir constructor yapısı bulunmaktadır. Bu constructor direkt sınıf adı tanımlanırken sınıf adının yanında tanımlanır.

Fakat primary constructor ile oluşturduğumuz bir sınıftan üretilen nesnelerin özelliklerine direkt ‘.’ operatörü ile ulaşamayız. Diğer dillerdeki private özellikler gibi düşünebiliriz.

Bu aşamada karşımıza encapsulation (kapsülleme) kavramı çıkmaktadır. Buyrun onu da bir alt başlıkta inceleyelim.

Initialize (init)

Encapsulation konusuna geçmeden önce Kotlin’de karşımıza çıkan init metodundan bahsedelim. Bu metod constructor metod ile benzerlik göstermektedir fakat ikisinin de kullanım şekli aslında farklıdır.

init metodu sınıftan bir nesne üretildiğinde çalışır ve genelde ilgili nesne üretildiğinde yapılmasını istediğimiz işlemleri init metodu içerisine yazarız.

Bir üst başlıkta (primary constructor) oluşturulan nesnenin özelliğine erişemediğimizden bahsetmiştik. Fakat gördüğünüz gibi aslında o nesnenin özelliğini set etmiş bulunuyoruz.

Bir üst resimde nesne üretilirken çağrılan init metodunun çıktısı

Kapsülleme (Encapsulation)

Kapsülleme kısaca oluşturduğumuz nesneye dışarıdan müdahaleyi sınırlamak için kullanılır diyebiliriz. Sınıfa tanımladığımız özelliğin başına private ön ekini kullanırız ve bu sayede dışarıdan bu sınıfın private olan özelliklerine erişilemez.

private ile özelliklere erişimi sınırladık
nesnemizin private olan özelliklerine erişemiyoruz

private özelliklere erişim sağlamak için sınıfa ait metodlar yazmamız gerekmektedir. Bunlar diğer dillerden bildiğimiz getter setter metodlarına benzemektedir.

getter setter metodları yazıldı ve kullanıldı
getter setter sayesinde objenin özelliklerine (private) erişim sağlandı

Inheritance (Miras Almak)

Bir sınıf oluşturduğumuzda başka bir sınıfta bu sınıfın özelliklerini kendisine miras olarak alır ve kendisine ait tanımlaması gereken özellikleri varsa onları tanımlayarak yoluna devam eder.

Şu şekilde düşünebiliriz. 5 adet sınıfımız olacak ve hepsinin neredeyse tüm özellikleri (property) aynı olacak. Sadece bazı sınıfların kendisine ait özellikleri olacak. Bu durumda kendimize bir adet parent class (ebeveyn sınıf) tanımlarız ve elimizdeki bu 5 sınıfı da bu parent class’ın sub class’ı (alt sınıf) olarak set eder ondan miras alırız. Bu sayede 5 adet sınıftaki ortak olan özellikleri ve metodları 5 kere tekrar tekrar yazmak yerine sadece bir kere parent class’ta yazarız. Tüm sınıflara tekrar ortak bir özellik ekleneceği zaman tekrar tüm sınıflara 5 kere property eklemek zorunda kalmaz sadece bir adet parent classımıza ekleme yaparak tüm sub class’lara ilgili özelliği eklemiş oluruz.

Kotlin’de bir sınıfı inheritance işlemine açık yapmak istersek sınıfı tanımlarken başında open ön ekini kullanmamız gerekmektedir.

Senaryomuz şu şekilde olacak. Developer adında bir parent class (ana sınıf) tanımlayacağız. Bu class’a bağlı 2 adet sub class olacak. WebDeveloper ve MobileDeveloper şeklinde. İşin özüne baktığımızda aslında ikisi de Developer sınıfına girmektedir.

Developer Class (Parent Class)

Developer adındaki parent class

WebDeveloper (Sub Class)

Bir de WebDeveloper adında sub class tanımlayalım. Bu sınıfta developer sınıfına ek olarak her developer’ı ilgilendirmeyen deploymentArea özelliği bulunacak. Bu özellik MobileDeveloper sınıfında bulunmadığından bunu yalnızca bu sınıfa özel tanımlamamız yeterli olacaktır.

Miras almak istediğimiz sınıfı ‘:’ ile miras alacağımız sınıfın yanına yazmamız gerekmektedir.

Fakat bu kod bloğu çalışmayacaktır. Bunun sebebi ise Developer sınıfının bir constructor’a sahip olmasıdır. Developer sınıfına ait olan name, age vd. özellikler WebDeveloper sınıfında da olacağından bu parametreleri WebDeveloper constructor’ında alıp parent yani üst sınıfın constructor’ına göndermemiz gerekmektedir. Aynı zamanda sadece WebDeveloper sınıfına ait olan deploymentArea özelliğini de WebDeveloper constructor’ında almamız gerekmektedir.

MobileDeveloper (Sub Class)

Aynı şekilde Sadece MobileDeveloper sınıfını da Developer sınıfından miras alarak üreteceğiz. Ne de olsa o da Developer sınıfındaki özelliklerin hepsini taşımaktadır. Fakat bu sınıfta da sadece MobileDeveloper sınıfını ilgilendiren marketName özelliği olacak ve bu parametreyi MobileDeveloper constructor’ında isteyeceğiz.

Bu sayede elimizdeki Developer’ları daha rahat sınıflandırabileceğiz. WebDeveloper sınıfından üretilen bir nesneyi ilgilendirmeyen marketName ve MobileDeveloper sınıfından üretilen nesneyi ilgilendirmeyen deploymentArea özelliklerini ayırmış olacağız.

Şimdi oluşturduğumuz sınıflardan birer nesne oluşturalım.

Inheritance sayesine kendimize bir adet parent class (Developer) oluşturduk ve bu sınıftan miras alarak sub class’lar (WebDeveloper, MobileDeveloper) oluşturduk. Bu sayede WebDeveloper’a ait olan deploymentArea özelliğini WebDeveloper’a, MobileDeveloper sınıfına ait olan uygulamasını yayınladığı marketin adı olan marketName özelliğini de MobileDeveloper’a kazandırmış olduk. Her iki sınıf içinde ortak olan name, age, programminLanguage ve osName özelliklerini de tekrar tekrar tanımlamak yerine tek seferde parent class’ta tanımlamış olduk.

Elimizdeki tüm sınıflar birer developer olduğuna göre hepsinin ortak metodu kod yazmak olamaz mı? Şimdi de tüm sınıflarımıza coding metodunu tanımlayalım.

Sadece parent class’ımızda coding metodunu tanımladık ve bu sayede kendisini miras alan tüm developerlarımız kodlama yetkinliğine sahip oldu :)

Polymorphism (Çok Biçimlilik)

Kısaca Polymorphism, aynı ismi kullanarak farklı işlemler yapabilmektir. Polymorphism kendi içinde 2'ye ayrılmaktadır. Aynı sınıf içerisinde aynı isimle farklı işlemler yapmaya static polymorphism denir. Farklı sınıflar içerisinden aynı isimle farklı işlemler yapmaya ise dynamic polymorphism denir.

Static Polymorphism

Developer sınıfımıza refactor adında 3 adet metod ekliyoruz.

Bu 3 metodu da aynı sınıf içerisinden aynı isimle çağırabiliyoruz fakat 3 metod da farklı işlemler gerçekleştiriyor. Aynı sınıf içerisinden aynı isimle farklı işlemler gerçekleştirdiği için buna static polymorphism denir.

Dynamic Polymorphism

Developer sınıfımıza develop adında bir metod ekliyoruz. Fakat bu metodun da başına open ön ekini yazıyoruz. Bunun sebebi ise nasıl ki bir sınıfı miras almaya açmak istersek başına open ekliyorsak burada da üzerine yazılmasına (overriding) izin verdiğimiz fonksiyonların başına open ön ekini yazıyoruz.

Şimdi de WebDeveloper ve MobileDeveloper sınıflarımızda bu develop metodunu override edelim.

Bu şekilde aynı isimle farklı sınıflardan farklı işlemler yaptırmaya da dynamic polymorphism denmektedir.

Super Anahtar Sözcüğü

super anahtar sözcüğü miras aldığımız bir üst sınıfı referans gösterir. super anahtar sözcüğü ile üst sınıfın metodlarına ve özelliklerine erişim sağlayabiliriz.

Yukarıdaki örneklerde Developer sınıfımıza develop metodunu kazandırmıştık ve bu metodu sub class’larımızda override etmiştik. Şimdi WebDeveloper sınıfımızda override ettiğimiz develop metoduna bir göz gezdirelim.

Bu metod çağrıldığında önce super anahtar sözcüğü sayesinde bir üst sınıfın develop metodunu çağıracak. Daha sonra kendi develop metodunu çalıştıracak. Çıktısı şu şekilde olacaktır.

Abstract Class (Soyut Sınıf)

Ortak özelliklere sahip sınıfları tanımlamak için kullanılan sınıflara Abstract Class denmektedir. Abstract Class’lardan nesneler oluşturulamamaktadır. Abstract sınıfları tanımlarken başlarına ‘abstract’ ön eki yazılmalıdır.

People adında bir soyut sınıf tanımlayalım ve bu sınıfın özelliklerini Person adındaki sınıfa kazandırıp Person sınıfından bir nesne oluşturalım.

Fakat People abstract sınıfımızdan bir nesne üretmek istersek hata ile karşılaşacağız. Bunun sebebi abstract class’ların kalıtım (inheritance) özelliği ile diğer sub class’lara ilgili özellikleri ve metodları aktarma görevini görmesidir. Fiziksel olarak değişkenlere veya entity’lere atanamazlar.

Interface (Arayüz)

Kendisini miras alan sınıfların miras aldığı Interface’in metodlarını override ederek kullandığı yapılardır. Interface’ler tanımlanırken interface ön eki kullanılır. Farklı sınıflar için aynı isimlerle farklı işlemler yapabiliriz.

Yukarıda Islem adında bir interface tanımladık. Dikkatiniz üzere metodların içerisini doldurmadık. Kendimize bir nevi bir iskelet çıkardık fakat içini boş bıraktık. Miras alan sınıflar bu iskeletin üzerine kendi yapılarını inşa edecekler.

Matematik adında bir sınıf oluşturalım ve Islem arayüzünden miras alalım. Aslında bu miras alma işlemi değildir, Interface metodlarını override ederiz ve buna implementation denmektedir.

Override etmek istemediğimiz metodlar olabilir. Bu metodları ise interface içerisinde default olarak tanımlamalıyız. Aşağıdaki örnekte bol metodu default olarak tanımlanmıştır.

Dilim döndüğünce Kotlin ile OOP anlatmaya çalıştım. Umarım faydalı olmuştur ^^

--

--

Baysan
Baysan

Written by Baysan

Lifelong learner & Developer. I use technology that helps me. mebaysan.com

Responses (1)