🐋OOP
Bir dil encapsulation, inheritance, abstraction, polimorfizm özelliklerini sağlıyorsa, bu dile OOP mantığı vardır deriz. Bir çok modern proglama dilinde(kotlin, python, swift, java…) OOP mantığı vardır.
Gerçek hayatta gördüğümüz her şeyi koda dökme olarak tanımlayabiliriz. Kod yazma sürecimizde istekler değişebilir, yeni şeyleri kodlarımıza eklememiz gerekebilir. İşte bu gibi durumlarda en az yerde değişiklik yapıp kodu düzenlememiz gerekir. Bu duruma kod sürdürülebilirliğide denir.
Bir programlama prensibidir. Kod okunurluğunu arttıran, daha az kod yazmamızı sağlayan prensibler şeklinde düşünebilirsiniz.
➡️Encapsulation
Bir classın değişkenlerinin private da tutup bunlara erişmek için fonksiyonlarına publicte tutmakdır. Başta çok gereksizde gelse bu kısım aslında hayat kurtarıyor. Sınıfın değişkenlerini her yerde rahatlıkla ulaşmak güvenli değil. Bunlara fonksiyon yardımı ile erişmek, o değişken özelliklerini istediğimiz şekilde kısıtlama özelliği de getiriyor bize.
class BankAccount {
private var result: Double = 0.0 // private olarak erişilebilen bir veri üyesi
fun saveMoney(amount: Double) {
// hesaba para yatırma işlemi
result += amount
}
fun spentMoney(amount: Double) {
// hesaptan para çekme işlemi
if (amount <= result) result -= amount
else println("Üzgünüm yeterli bakiyeniz yok..")
}
fun getResult(): Double {
// hesap bakiyesini alma işlemi
return result
}
}
fun main(){
val bank = BankAccount()
bank.saveMoney(20.0)
bank.spentMoney(30.0)
println(bank.getResult())
}
Burada visibility modifier lardan da biraz bahsedelim.
Kotlin de herhangi bir visibility modifier tanımlamazsanız default değeri public tir.
public -> Her yerden erişebilirsiniz
private-> Classın dışından tanımladığın yapıya erişemezsin
protected-> Korunmuş olarak çevirebiliriz. Sadece miras yoluyla erişime izin verir. Yani bunu miras alan sınıflarda kullanılabilecek.
internal-> Aynı modülün için de her yerde ulaşabilirsin. Modül dışındaki yapı da ulaşamazsın.
➡️İnheritance
Bir sınıfın başka bir sınıftan özelliklerini ve davranışlarını miras almasına olanak tanır.
open class Button(val label: String) {
open fun printDescription() {
println(label)
}
}
class ImageButton(label: String, val image: String): Button(label) {
override fun printDescription() {
println("$label with image $image")
}
}
❓Inheritance kullanmak yerine o sınıftan obje üretip metoda erişebilirim. Evet, doğru ama bazen o methodu evrimleştirmek isteyebilirsin işte burada bana gerekli.
✨ Kotlin de bütün classlar default olarak final demek. Final miras alınamaz demek. O yüzden miras almak için open keywordünü kullanmalısınız.
✨ Base class tan inherit edilen metotlar sub classta override ön eki ile kullanılır. Ayrıca base class taki her fonksiyon, sub classta kullanılma zorunluluğu yoktur. Tamamen ihtiyaca bağlı olarak değişir.
✨ Bir yerden miras aldığın zaman, mirasın constructorı varsa primary constructor oluyor. Dolayısı ile burada secondary constructor verilemiyor. Primary constructor bu gibi durumlarda mirasın şartını yerine getirmek için lazım.
✨ Her child class üst classtan daha fazla şey biliyor ve yapabiliyor. Yetenekleri daha fazla olduğu için child class ların daha fazla bellek harcadığını gösterir. Yani üst class lar daha performanslı çalışır.
✨ Miras alırken constructorların dan birine uygun olacak şekilde parametre vermeliyiz.
Kotlin de üretilen bütün classlar any class tan miras alır.
Kotlin deki sınıfların “Any” sınıfından miras alması, sınıfların ortak özelliklerini içerir. Örneğin, tüm sınıfların “equals()”, “hashCode()” ve “toString()” gibi bazı yöntemleri bulunur.
Kalıtımın dikkatlice kullanılması gereken bir özellik olduğunu unutmamak önemlidir. Aşırı kullanımı, karmaşık sınıf hiyerarşilerine ve sıkı bağımlılıklara yol açabilir ve kodun karmaşıklığını artırabilir. Bu nedenle, kalıtımın mantıklı ve gerektiğinde kullanılması, doğru tasarlanmış sınıf hiyerarşileri oluşturulması önemlidir.
Abstract konusuna geçmeden bir şeye daha değinelim burada. “button” objesinin türünü Button olarak belirledik.
❓Neden bunu ImageButton yapmak yerine böyle yapalım ki zaten onun fonksiyonlarına erişmeyecekmiyiz bunun bize tam olarak artısı nedir?
👻Bu duruma polimorfizm deniyor(Daha detaylı anlatacağım). Kısaca referance tutması bizim işimizde gelir diyelim. Ayrıca da ImageButton altındaki override fonksiyonu silsek de bu kod o zaman Base class ın altındaki metodu çalıştırır. Özellik gibi özellik🔥.
fun main() {
val button:Button = ImageButton("write","something")
button.printDescription()//write with image something
//write
}
➡️Abstract
✨ Gövdeli veya gövdesiz metot içerirler.
✨ Abstract sınıflar abstract metot içerme zorunluluğu yoktur. Ama abstract metot içeren sınıfın abstract class olması gerekmektedir.
✨ Abstract sınıflardan bir obje üretemeyiz
✨ Abstract sınıflar bir base sınıf olarak genelde kullanılır.
✨ Constructor içerebilir.
✨ Gövdeli ve gövdesiz methotları içerebilir. Gövdeli metotlarında abstract keywordu kullanılmaz. Abstract keywordu kullanılanları implement etmek zorundasın sub classına.
✨ Bir class olduğu için tek bir sefer kalıtım sağlar.
✨ is-a özelliğine sahiptir.(örn:Memur bir personeldir.)
abstract class GeometricShape {
abstract fun calculateArea(): Double
abstract fun calculateEnvironment(): Double
}
class Rectangle(private val length: Double, private val width: Double) : GeometricShape() {
override fun calculateArea(): Double {
return length * width
}
override fun calculateEnvironment(): Double {
return 2 * (length + width)
}
}
class Circle(private val yariCap: Double) : GeometricShape() {
override fun calculateArea(): Double {
return Math.PI * yariCap * yariCap
}
override fun calculateEnvironment(): Double {
return 2 * Math.PI * yariCap
}
}
fun main() {
val dikdortgen:GeometricShape = Rectangle(5.0, 10.0)
println(" ${dikdortgen.calculateArea()}")
println(" ${dikdortgen.calculateEnvironment()}")
}
➡️İnterface:
Tanımladığımız yapının interface olduğunu belirtmek için isminin önüne “I” harfini getirmek ve implemente edildiği sınıf isminin sonuna “Impl” getirmek bir best-practice örneği olacaktır.
interface IProduct {
fun productList( ) : List<String>
fun productAdd(data: String )
}
class WomanProductImpl : IProduct {
override fun productList(): List<String> {
return arrayListOf("canta","elbise")
}
override fun productAdd(data: String){
println("$data eklendi")
}
}
✨ İnterface ler bize maliyet oluşturmaz.
✨ İnterface ler belirli düzene göre programı yürütmemizi sağlar.
✨ İnterface içindeki fonksiyonları inherit aldığı class larda override etmek zorunludur.
✨ Sınıf olmadıkları için constructor içermezler.
✨ Sınıflar tek bir sınıftan kalıtım alabilirken, İnterfaceler sayesinde çoklu kalıtımı desteklemektedirler.
✨ Obje üretilemez.
✨ Gövdeli ve gövdesiz methotları içerebilir. Kalıtım edildiğinde bir sınıfa gövdesiz metotları override etmesi zorunludur.
✨ can-do ilişkisine göre çalışır.(Product Ekleyebilir, Listeleyebilir…)
✨ Interface içerisinde her şey public olmalıdır.
❓Yukardaki kod bloğunu görünce akla şu gelebilir. Neden interface oluşturuyoruz ki bunun yerinde direkt kodları class içerisine yazalım olsun bitsin. Başlangıçta mantıklı gibi görünsede bu durum, sonra başımızı ağrıtır. Mesala diyelim ki şirketiniz dediki ben artık Kadın kıyafetleri satmak istemiyorum bunun yerine erkek kıyafetlerine geçiş yapalım.
class ManProductImpl:IProduct{
override fun productList(): List<String> {
return arrayListOf("ayakkabı","ceket")
}
override fun productAdd(data: String) {
println("$data eklendi")
}
}
fun main(){
val woman:IProduct = WomanProductImpl()// bu satırı kaldırıp
val man:IProduct = ManProductImpl()// bunu ekleyelim
man.productAdd("Kemer")
}
Gördüğünüz gibi bize o kodu sil tekrar yaz yapmak yerine çok az değişiklikle işlerimizi hallettik.
➡️Polymorphism
Türkçe çevirisi çok biçimliliktir. Bir reference alttaki sub classlara referans olduğunda farklı şekilde çalışması şeklinde tanımlanabilir. Polimorphism sayesinde kodumuza esneklik katmış oluruz. Direkt bağlantıyı azaltarak daha güvenli hale getiririz.
2 tür polimorfizm vardır.
- Overloading(Compile Time)
- Overriding(Run Time)
👉🏻Overloading(Compile Time)
Aynı isme sahip 2 tane fonksiyon ismi yazamazsınız. Ancak parametre sayısını yada türünü değiştirip yazabilirsin. Buna da overloading deniyor.
fun sum(param1: Int) {
// write something
}
fun sum(param1: String) {
// ...
}
fun sum(param1:Int, param2:Int){
// ...
}
👉🏻Overriding(Run Time)
Yukarda kod bloglarındada gördük örneklerini. Kod blogunu ezerek içini kendisi doldurması şeklinde tanımlayabiliriz.
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
}
}
Burada dikkat edilmesi gereken nokta Base’ın reference alıp, üretildiği sub class özelliklerini göstermesi.
Kendinize iyi bakın başka bir yazı da görüşmek üzere👋🏻
Kaynaklar🎃