Anasayfa / JAVA / Java’da Abstraction (Soyutlama) Nedir?

Java’da Abstraction (Soyutlama) Nedir?

Nesne Yönelimli Programlama (OOP) mantığıyla Java’da kod yazmaya başladığınızda, sistemlerinizi daha düzenli, güvenli ve esnek hale getiren temel prensiplerle karşılaşırsınız. Daha önceki yazılarımızda Sarmalama (Encapsulation), Kalıtım (Inheritance) ve Çok Biçimlilik (Polymorphism) konularını incelemiştik. Şimdi ise bu prensiplerin en önemlilerinden biri olan, karmaşık sistemleri basitleştirerek yönetmemizi sağlayan Abstraction (Soyutlama) kavramını ilk defa öğrenecekler için en temelden başlayarak inceleyeceğiz.


1. Abstraction (Soyutlama) Ne Demektir?

İnsanlar, çevrelerindeki karmaşıklığı “soyutlama” (abstraction) yoluyla yönetirler. Günlük hayattan bir örnek vermek gerekirse, bir otomobili on binlerce bireysel parçadan (vidalar, kablolar, silindirler vb.) oluşan bir yığın olarak düşünmeyiz; onu kendine özgü davranışları olan, iyi tanımlanmış tek bir nesne (araba) olarak algılarız.

Bir arabayı kullanarak markete gitmek istediğinizde, direksiyon sisteminin, motorun veya şanzımanın arka planda mekanik olarak nasıl çalıştığı gibi karmaşık detayları bilmek zorunda değilsiniz. Sadece direksiyon, gaz ve fren pedallarını kullanarak arabadan bir bütün olarak faydalanabilirsiniz.

İşte yazılım dünyasında ve Java’da Abstraction (Soyutlama) tam olarak bu amaca hizmet eder: Gereksiz ve karmaşık iç detayları gizleyerek, kullanıcıya (veya diğer kodlara) sadece ihtiyaç duydukları özellikleri ve işlevleri sunmak. Bu sayede detaylarda boğulmadan büyük ve karmaşık yazılım sistemleri inşa edebiliriz.


2. Java’da Soyutlamaya Neden İhtiyaç Duyarız?

Bazen bir “Ata Sınıf” (Superclass) tasarlarsınız ve bu sınıfın, kendisinden türeyecek tüm alt sınıflar için genel bir şablon olmasını istersiniz. Ancak bu ata sınıf, bazı metotların içini (nasıl çalışacağını) kendisi dolduramaz.

Örneğin, ekrana çeşitli şekiller çizen bir program yazdığınızı düşünün. Elinizde genel bir Sekil sınıfı ve bu sınıfın içinde bir alanHesapla() metodu olsun. Ancak tanımsız, genel bir “Şekil” kavramının alanını hesaplamak mantıksal olarak imkansızdır, çünkü şeklin bir daire mi, kare mi yoksa üçgen mi olduğu belli değildir.

Eğer bu metodu boş bir şekilde tanımlarsanız, alt sınıfların bu metodu kendine göre yazmasını (override etmesini) garanti altına almanız gerekir. Aksi halde alt sınıf, bu metodu yazmayı unutursa program anlamsız hale gelir. İşte Java, bu sorunu çözmek ve bir şablon oluşturmak için Abstract (Soyut) metotları ve sınıfları kullanır.


3. Abstract (Soyut) Metotlar ve Sınıflar

Java’da soyutlama yapmanın bir numaralı yolu abstract (soyut) anahtar kelimesini kullanmaktır.

Soyut Metot (Abstract Method): Bir alt sınıf tarafından kesinlikle yeniden yazılması (ezilmesi/override edilmesi) gereken, ancak gövdesi (süslü parantezlerle belirlenen iç kısmı) olmayan metotlardır. Şu şekilde tanımlanırlar: abstract tip metotAdi(parametreler); Gördüğünüz gibi metodun sonunda süslü parantez {} yoktur, doğrudan noktalı virgül ; ile biter.

Soyut Sınıf (Abstract Class): İçinde bir veya daha fazla soyut metot barındıran herhangi bir sınıf, kendisi de abstract olarak tanımlanmak zorundadır. Soyut sınıfların en kritik özellikleri şunlardır:

  • Nesnesi Oluşturulamaz: Soyut bir sınıf eksik bir kalıp olduğundan, new anahtar kelimesi kullanılarak doğrudan nesnesi oluşturulamaz (instantiate edilemez). Eğer Sekil s = new Sekil(); yazmaya çalışırsanız derleyici hata verir. Çünkü ortada tam tanımlanmamış bir yapı vardır.
  • Alt Sınıflar İçin Zorunluluk: Soyut bir sınıftan kalıtım alan (miras alan) bir alt sınıf, eğer kendisi de soyut olmak istemiyorsa, ata sınıfındaki tüm soyut metotların içini doldurmak (implement etmek) zorundadır.
  • Hem Soyut Hem Somut Metot Barındırabilir: Soyut sınıfların içi tamamen boş olmak zorunda değildir. İsterseniz içlerine kodları tamamen yazılmış normal (somut) metotlar da ekleyebilirsiniz.

4. Adım Adım Kod Örneği: Şekillerin Alanını Hesaplamak

Yukarıdaki teorik bilgileri pekiştirmek için Sekil isimli bir soyut sınıf tasarlayalım. Daha sonra bu sınıftan Dikdortgen ve Daire adında somut (nesnesi oluşturulabilir) sınıflar türetelim.

// 1. ADIM: Ata Sınıfı Soyut (Abstract) Olarak Tanımlıyoruz
abstract class Sekil {

    // Normal (somut) bir değişken
    String renk;

    // Normal (somut) bir metot. İçi dolu, çalışabilir durumda.
    public void renkSoyle() {
        System.out.println("Bu şeklin rengi: " + renk);
    }

    // SOYUT (ABSTRACT) METOT: Gövdesi yok!
    // Alt sınıflar kendi şekillerine göre bu hesabı yapmak zorunda.
    abstract double alanHesapla();
}

Şimdi bu soyut sınıftan kalıtım alacak alt sınıfları yazalım:

// 2. ADIM: Alt Sınıfları Oluşturuyoruz

// Dikdörtgen bir Şekil'dir.
class Dikdortgen extends Sekil {
    double kisaKenar;
    double uzunKenar;

    // Kurucu metot
    Dikdortgen(double kisa, double uzun, String r) {
        this.kisaKenar = kisa;
        this.uzunKenar = uzun;
        this.renk = r; // Ata sınıftan gelen değişken
    }

    // ZORUNLU GÖREV: Soyut metodu eziyoruz (Override)
    @Override
    double alanHesapla() {
        return kisaKenar * uzunKenar; // Dikdörtgen alan hesabı
    }
}

// Daire de bir Şekil'dir.
class Daire extends Sekil {
    double yaricap;
    final double PI = 3.14;

    Daire(double y, String r) {
        this.yaricap = y;
        this.renk = r;
    }

    // ZORUNLU GÖREV: Soyut metodu eziyoruz (Override)
    @Override
    double alanHesapla() {
        return PI * yaricap * yaricap; // Daire alan hesabı
    }
}

Son olarak bu kodları çalıştıracak AnaProgram (Main) kısmına bakalım:

public class AnaProgram {
    public static void main(String[] args) {

        // HATA VERİR: Sekil nesnesi = new Sekil(); // Soyut sınıfın nesnesi yaratılamaz!

        // Çok Biçimlilik (Polymorphism) kullanarak nesneleri oluşturuyoruz:
        Sekil sekil1 = new Dikdortgen(4, 5, "Mavi");
        Sekil sekil2 = new Daire(3, "Kırmızı");

        sekil1.renkSoyle(); // Çıktı: Bu şeklin rengi: Mavi
        System.out.println("Dikdörtgenin Alanı: " + sekil1.alanHesapla()); // Çıktı: 20.0

        sekil2.renkSoyle(); // Çıktı: Bu şeklin rengi: Kırmızı
        System.out.println("Dairenin Alanı: " + sekil2.alanHesapla()); // Çıktı: 28.26
    }
}

Bu Örnekte Ne Oldu? Biz genel bir Sekil sınıfı yarattık. Sistemin geri kalanı (Ana Program), şeklin kendi alanını nasıl hesapladığı formülüyle (iç detaylarla) hiç ilgilenmedi. Sadece alanHesapla() komutunu çağırdı ve sonucu aldı. Dikdörtgen ve Daire kendi içlerinde gizli tutulan karmaşık işlemlerle (Abstraction mantığıyla) bu hesabı yaptı. İleride sisteme “Üçgen” eklemek isterseniz, var olan kodlara hiç dokunmadan sadece yeni bir sınıf ekleyip alanHesapla metodunu kurgulamanız yeterli olacaktır.


5. Arayüzler (Interfaces) ile Tam Soyutlama

Java’da bir sınıfın “ne yapması gerektiğini” belirtip, “nasıl yapacağını” tamamen gizlemenin (soyutlamanın) bir diğer yolu da Arayüz (Interface) kullanmaktır. Arayüzler, bir sınıfın dış dünyaya sunduğu yüzünü, gerçekleştiriminden (implementation) tamamen soyutlar.

Bir Arayüz, kendisini kullanacak sınıflar için bir “sözleşme” (contract) niteliği taşır. Sisteme dahil olmak isteyen herhangi bir sınıf, bu arayüzde belirtilen metotları yazmak zorundadır. Geleneksel Java’da (Java 8 öncesi) arayüzlerin içindeki metotların tamamı gizlice public ve abstract (gövdesiz soyut) olarak kabul edilir.

Neden Soyut Sınıf Yerine Interface Kullanılır? Java dilinde bir sınıf yalnızca tek bir ata sınıftan miras (kalıtım) alabilir (Multiple Inheritance’a izin verilmez). Ancak bir sınıf, sınır olmaksızın istediği kadar arayüzü (Interface) uygulayabilir (implement edebilir). Bu sayede sınıflara birbirlerinden tamamen bağımsız farklı soyut yetenekler (örneğin hem Cizilebilir hem de HareketEdebilir arayüzleri) kazandırmak mümkün olur. Arayüzler, nitelik (state) tutmayan, tamamen davranış (behavior) tanımlayan yapı taşlarıdır.

Özet Olarak

Abstraction (Soyutlama), projenizdeki kodların gereksiz detaylarını dış dünyadan saklama ve yalnızca gerekli olan işlevleri bir “şablon” veya “sözleşme” dahilinde sunma işlemidir.

  • Eğer bir nesnenin sadece ortak davranış şablonunu belirlemek ancak bazı metotların içini alt sınıflara bırakmak istiyorsanız Abstract (Soyut) Sınıfları kullanmalısınız.
  • Eğer bir sınıfın ne yapması gerektiğini kurallara bağlamak, tamamen gövdesiz bir sözleşme hazırlamak ve çoklu kalıtım (birden fazla yetenek ekleme) yapmak istiyorsanız Interface (Arayüzleri) kullanmalısınız.

Java öğrenme sürecinizde bu iki aracı doğru kullanmak, kodlarınızı modüler, esnek ve gelecekte kolayca genişletilebilir hale getirerek sizi profesyonel bir yazılımcı yapacak en büyük adımdır.

Etiketlendi:

Cevap bırakın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir

DERSLER