Abstract nedir, ne zaman kullanılır?

Abstract sınıflar içerisinde normal yani içi dolu metodların,değişkenlerin ve interface’lerdeki gibi abstract (boş) metodların tanımlanabildiği yapılardır.Bu sınıflar new kelimesi ile oluşturulamazlar.

Hemen uygulamaya geçelim örnek olarak arabalarla alakalı geliştirdiğimiz bir uygulamamız olsun. Sistemimizde bulunan çeşitli marka arabalara ait bazı özellikleri ekranda göstersin. Arabaların ağırlık,renk,model gibi ortak özellikleri ve cant kalınlığı,devir sayısı gibi kendilerine has özellikleri mevcut. Bunu ifade eden aşağıdaki gibi kodumuzu yazalım.

class Araba{
    private int agirlik;
    private String renk;
    private String model;

    public int getAgirlik() {
        return agirlik;
    }
    public void setAgirlik(int agirlik) {
        this.agirlik = agirlik;
    }
    public String getRenk() {
        return renk;
    }
    public void setRenk(String renk) {
        this.renk = renk;
    }
    public String getModel() {
        return model;
    }
    public void setModel(String model) {
        this.model = model;
    }
}
class Mercedes extends Araba{
    private int cantKalinligi;

    public int getCantKalinligi() {
        return cantKalinligi;
    }
    public void setCantKalinligi(int cantKalinligi) {
        this.cantKalinligi = cantKalinligi;
    }
}
class Ford extends Araba{
    private int devirSayisi;

    public int getDevirSayisi() {
        return devirSayisi;
    }
    public void setDevirSayisi(int devirSayisi) {
        this.devirSayisi = devirSayisi;
    }
}
class KullaniciEkrani{
    public void goster(Araba[] arabalar){
        for (int i = 0; i < arabalar.length; i++) {
            Araba araba = arabalar[i];
            System.out.println("Agirlik : "+araba.getAgirlik());
            System.out.println("Model : "+araba.getModel());
            System.out.println("Renk : "+araba.getRenk());
        }
    }
}
class AnaProgram{
    public static void main(String[] args) {
        Araba ford =new Ford();
        ford.setAgirlik(1000);
        ford.setModel("Fiesta");
        ford.setRenk("Gri");
        Araba mercedes=new Mercedes();
        mercedes.setAgirlik(2000);
        mercedes.setModel("E200");
        mercedes.setRenk("Siyah");

        Araba arabalar[]=new Araba[]{mercedes,ford};
        KullaniciEkrani ekran =new KullaniciEkrani();
        ekran.goster(arabalar);
    }
}


Yukarıdaki kodda gördüğünüz gibi Polymorphism sayesinde KullaniciEkrani sınıfı arabaların markalarından habersiz hepsini gösterebiliyor. Araba sınıfından türeyen her sınıf KullaniciEkrani sınıfında gösterilebiliyor.Buraya kadar gayet güzel. Şimdi müşteri bizden bu ekranda araçların saatte kaç litre benzin yaktıklarını da göstermemizi istedi.Fakat burada şöyle bir problem var her marka arabanın kaç litre benzin yaktığı kendi ağırlığına göre farklı hesaplanıyor.Araba sınıfına saatte yaktığı litre diye değişken eklesek olmayacak çünkü Mercedes bunu hesaplamak için farklı katsayı ile çarpıyor Ford farklı sayı ile çarpıyor.İşte bu noktada yardımımıza Abstract kavramı yetişiyor ve kodumuzu şöyle değiştiriyoruz.

abstract class Araba{
    private int agirlik;
    private String renk;
    private String model;

    public int getAgirlik() {
        return agirlik;
    }
    public void setAgirlik(int agirlik) {
        this.agirlik = agirlik;
    }
    public String getRenk() {
        return renk;
    }
    public void setRenk(String renk) {
        this.renk = renk;
    }
    public String getModel() {
        return model;
    }
    public void setModel(String model) {
        this.model = model;
    }

    public abstract int saateYaktigiBenzinLitresi();
}
class Mercedes extends Araba{
    private int cantKalinligi;

    public int getCantKalinligi() {
        return cantKalinligi;
    }
    public void setCantKalinligi(int cantKalinligi) {
        this.cantKalinligi = cantKalinligi;
    }

    public int saateYaktigiBenzinLitresi() {
        return getAgirlik()*2;
    }
}
class Ford extends Araba{
    private int devirSayisi;

    public int getDevirSayisi() {
        return devirSayisi;
    }
    public void setDevirSayisi(int devirSayisi) {
        this.devirSayisi = devirSayisi;
    }

    public int saateYaktigiBenzinLitresi() {
        return getAgirlik()*1;
    }
}
class KullaniciEkrani{
    public void goster(Araba[] arabalar){
        for (int i = 0; i < arabalar.length; i++) {
            Araba araba = arabalar[i];
            System.out.println("Agirlik : "+araba.getAgirlik());
            System.out.println("Model : "+araba.getModel());
            System.out.println("Renk : "+araba.getRenk());
            System.out.println("Yaktigi Lt. Benzin : "+araba.saateYaktigiBenzinLitresi());
        }
    }
}
class AnaProgram{
    public static void main(String[] args) {
        Araba ford =new Ford();
        ford.setAgirlik(1000);
        ford.setModel("Fiesta");
        ford.setRenk("Gri");
        Araba mercedes=new Mercedes();
        mercedes.setAgirlik(2000);
        mercedes.setModel("E200");
        mercedes.setRenk("Siyah");

        Araba arabalar[]=new Araba[]{mercedes,ford};
        KullaniciEkrani ekran =new KullaniciEkrani();
        ekran.goster(arabalar);
    }
}

Gördüğünüz gibi yukarıdaki kodda Araba sınıfına public abstract int saateYaktigiBenzinLitresi(); adında bir soyut metod ekledik. Bir sınıfın abstract metod bulundurabilmesi için Abstract bir sınıf olması gerekir. İkinci olarak Araba sınıfını abstract olarak değiştirdik. Bunu yapmamızın sebebi Araba sınıfı tarafından bu hesaplama işleminin nasıl yapılacağının bilinmemesidir. Yani bu hesaplamayı kendi yapmayıp kendinden türeyen sınıfların yapmasını şart koşmuştur. Bu hesaplamayı gördüğünüz gibi her marka araba sınıfı kendi katsayılarına göre kendi içinde yapıyor. Yukarıda Mercedes ve Ford sınıflarında saateYaktigiBenzinLitresi() metodunun nasıl farklı şekillerde yazıldığını gördünüz.Bu sayede KullaniciEkrani sınıfında Araba sınıfındaki saateYaktigiBenzinLitresi() metodunu çağırdığında her marka araba için kendi içlerinde yazdıkları saateYaktigiBenzinLitresi() metodu çalışacaktır.

Dikkat ederseniz uygulamamızada new kelimesi ile Araba nesnelerini zaten oluşturmuyorduk. Bizim oluşturduğumuz nesneler new Merceses,Ford gibi nesnelerdi.O yüzden Araba sınıfını normal sınıf tanımlamak tasarım bakımından zaten anlamsız.Burada Araba sınıfı kodun diğer kısımlarından Mercedes ve Ford gibi belirgin sınıfları soyutlamak ve sınıflarındaki ortak özellikleri(agirlik,renk,model) kodu tekrar yazmamak için oluşturulmuş bir soyut sınıf.Kod olarak baktığımızda KullaniciEkrani sınıfı Mercedes ve Ford sınıflarından habersiz sadece Araba sınıfını biliyor.

Gördüğünüz gibi Abstract sınıflar daha çok nesneler arasındaki ortak özelliklerin veya metodların bir üst sınıfta toplanarak kod tekrarını önlemek ve kodu diğer sınıflardan soyutlayarak değişimin etkisini en alt düzeye indirmektir.

Aklınızda bulunsun herhangi bir sınıfı Abstract bir sınıftan türetmek istediğinizde genel olarak dikkat etmeniz gereken durum aralarında IS-A özelliği olmasıdır.Yani Mercedes bir Arabadır, Ford bir Arabadır diyebiliriz.

Kısaca özetleyecek olursak Abstract sınıflar aralarında IS-A(..dır,..dir) ilişkisi olan sınıflardaki ortak özelliklerin ve metodların soyut üst sınıfda toplanması ondan sonra uygulamada bu soyut sınıfın kullanılarak uygulamanın diğer sınıflarından habersiz halde çalışmasını sağlamaktır. Nesneye yönelik tasarımda uygulamayı iyi soyutlamalar üzerine kurmak oldukça önemlidir. Uygulamada soyutlama oluşturmak için kullanılan önemli kavramlardan olan Abstract sınıfları küçük bir örnekle inceledik. Yakında diğer soyutlama mekanizması olan Interface kavramına değineceğiz. Tekrar görüşmek üzere…

30 thoughts on “Abstract nedir, ne zaman kullanılır?

  1. Tolga

    Eline saglik güzel bir makale olmus. Interface makalesi ile beraber yada daha sonra nezaman abstract ne zaman interface kullanmak gerekir diye bir makalede yararli olur diye düsünüyorum.
    Tesekkürler, kolay gelsin…

  2. M. Cihat Altuntaş Post author

    Aslında interface ile birlikte hazırlıyordum fakat makale çok uzun olunca parçalasam daha iyi olur diye düşündüm.Yakında interface ile alakalı makale hazır olur ondan sonra ikisi arasındaki farklar hakkında bir makale hazırlarım. Asıl ilgin için teşekkür ederim kal sağlıcakla…

  3. Seda

    Yazilarinizi okudum.Paylastiginiz bilgiler icin cok tesekkur ederim.Yazilarinizin devamini bekliyorum.Iyi calismalar.

  4. Cosku Cimen

    peki diyelimki ben abstract tanımını bilmiyordum ve aynı amacı oluşturmak için public virtual int saateYaktigiBenzinLitresi() kullanıyordum. ve ford ve mercedes sınıflarında bunu implement ediyordum.sonra abstract classları ogrendim ama abstract kullanmam için nedenim nedir? (abstract kullanmanın araba classının direk instanceını almamı engellemesi hariç)

  5. M. Cihat Altuntaş Post author

    Evet güzel bir noktaya değinmişsin:) Şimdi şöyle açıklamaya çalışayım dediğin gibi yaparsan yani diyelim bir virtual metod tanımladık ve Araba sınıfımız Abstract değilde normal bir sınıf.Öncelikle bu virtual metodun içini doldurmak zorundayız.Peki bu içi dolu metod ne geri döndürecek?Muhtemelen “return 0″ tarzı bişey yazıcaz. Tabi sınıfımız abstract olmadığı için herhangi biryerde Araba sınıfını kullanıp o metodu çağırdığında 0 dönecek yani olmaz değil ama pek iyi bir çözüm değil çünkü hataya açık bir yapı. Daha detaylı olarak Liskov substitution prensibi içerisinde anlatabilirim.

  6. selim dağ

    Merhaba Cihat Bey,

    Gerçekten çok güzel makaleleriniz bulunuyor. Özellikle TTD ile alakalı makalelerinizi çok beğendim. Gayet açık, anlaşılır bir dille ele alıyorsunuz. Teşekkürler.

    saateYaktigiBenzinLitresi metodunun gövdesine baktığımızda tüm türetik sınıflarda da aslında bir kod tekrarı olduğunu görüyoruz. Bunu gidermek için abstract sınıfımızda getKatSayisi isimli bir abstract metot ekleyip bunu türetik sınıflarımızda override edip abstract sınıfımızın saateYaktigiBenzinLitresi metodunda da getAgirlik()’dan dönen değeri katSayisi() metodundan dönen değere çarpıp döndürsek nasıl olur(abstract özelliğini kaldırarak)?

    class Araba
    {

    public abstract int getKatSayisi();
    public int saateYaktigiBenzinLitresi() {
    return getAgirlik()*getKatSayisi();
    }
    }

    class Mercedes extends Araba{
    ….
    public override getKatSayisi(){
    return 2;
    }

    }

    class Ford extends Araba{
    ….
    public override getKatSayisi(){
    return 1;
    }
    }

  7. ahmet

    Tek bir yazı değil her makale çok doyurucu içeriğe sahip, o kadar özet olarak çarpıcı noktadan detayları vermişsiniz ki tebrik ederim.
    OOP nedir, şimdi anladım. Teşekkürler

  8. Pingback: Interface mi, Abstract mı? « Dumlupınar Üniversitesi ( DPÜ ) Bilgi ve Bilişim Kulübü

  9. Pingback: Anonymous

  10. ertan

    öncelikle merhaba sitenizi cok begendim,burayi nasil buldugma deginmek gerekirsede google java Abstract kelimelerini aratmak yeterli oldu…
    java ögrenmeye yeni baslayan biri olarak size kücük bir sorum olacakti…
    sinifi tanimlarken ben biraz fakliy yaptim..bu nedenle mi bilemiycem anaProgram kisminnda bu alani yapamadim:
    class AnaProgram{
    02 public static void main(String[] args) {
    03 Araba ford =new Ford();
    04 ford.setAgirlik(1000);
    05 ford.setModel(“Fiesta”);
    06 ford.setRenk(“Gri”);

    yaptigim sinif ise su sekilde;Ford sinifinida böyle yaptim.

    class Araba{
    private int agirlik;
    private String renk;
    private String model;

    public Araba(int agirlik,String renk,String model){
    this.agirlik=agirlik;
    this.renk=renk;
    this.model=model;

    public int getAgirlik() {
    return this.agirlik;
    }
    public String getRenk() {
    return this.renk;
    }
    public String getModel(){
    return this.model:
    }
    }

    umarim yardimci olabilirsiniz.simdiden cok tesekkürler =)

  11. furkan

    saateYaktigiBenzinLitresi() metodunu diğer classlardan çağırırken override özelliği ile çağırılması gerekmiyor mu? Örnekte düzenlerseniz okuyan arkadaşların kafası karışmaz.

    Makalelerinizin çok faydalı olduğunu düşünüyorum. Emeğinize sağlık…

  12. sakit

    Merhaba,
    benim bir sorum olucakdı yazının başında “Bu sınıflar new kelimesi ile oluşturulamazlar.” diye belirtmişsiniz ama programda bu sınıfdan bir dizi oluşturdunuz bu nasıl oluyor peki?(ana program satır 12 ” Araba arabalar[]=new Araba[]{mercedes,ford};”)

  13. Pingback: Interface mi, Abstract mı? | Yazılım Mühendisliği

  14. ibrahim halil

    public override int saateYaktigiBenzinLitresi()

    yapmamiz gerekiyor cunku override yapmayinca
    ‘ArabaAlma.Ford’ does not implement inherited abstract member ‘ArabaAlma.Araba.saateYaktigiBenzinLitresi()’ hayasi geliyor

  15. Pingback: Best Of 2008 | Yazılım Mühendisliği

Comments are closed.