Abstract Yapılar

Soyutlama (Abstraction) kavramı, fiziki yapılardan ziyade fikirlerle uğraşmaktır. Interface yazımızla bu kavrama giriş yapmış ve çok biçimlilikten söz ederek nesneye yönelik programlamanın iyi öğrenilebilmesi için özel olarak nelere dikkat etmemiz gerektiğine de değinmiştik.

Soyutlamada interfaceler ile birlikte bize yardımcı olan abstract yapılar bu yazımızın konusu olacak. Bu yazımızda 12 – Abstraction isimli bir proje yaratıp onun üzerinden ilerleyeceğiz. Projemizde ele alacağımız konu başlığını banka hesapları olarak belirleyelim. Hikayemiz ise, bir bankada çalışan ve hesaplarla ilgilenen bir ekibin, hesap tiplerini ürün olarak sunmak istemesi olsun. İlk görünüşe göre bir hesabın temel özelliklerini tanımlayan bir interface yaratmamız yeterli olacak. Gelin bu sorunu biraz daha şekillendirelim. Bu ekip, herhangi bir hesap tipindeki hesaptan, para çekilme işlemi gerçekleştiğinde belirli bir aksiyon olsun istiyor. Yani çalışan bir kod parçasının varlığına ihtiyaç duyuyoruz. Bildiğiniz gibi interface konusunda, java 8 öncesinde, bunun yapılamadığını söylemiştik. Zira buradaki ihtiyaç bir method gövdesinin varlığına işaret ediyor. İşte bu durumu çözmek için bir abstract class yaratacağız. Öncelikle sınıfımızı yaratıp, TestClass içerisinde biraz detaylarına inelim.

public abstract class AbstractAccount {

	private String accountName;
	private String iban;
	private double balance;

	public void changeBalance(double amount) {

		this.balance += amount;
		System.out.println("AbstractAccount.changeBalance()");
	}

	public String getAccountName() {
		return accountName;
	}

	public void setAccountName(String accountName) {
		this.accountName = accountName;
	}

	public String getIban() {
		return iban;
	}

	public void setIban(String iban) {
		this.iban = iban;
	}

	public double getBalance() {
		return balance;
	}

	public void setBalance(double balance) {
		this.balance = balance;
	}

}

Şimdi bu sınıfı kısaca inceleyelim. Görünüşte her şey bir sınıf ile aynı gözüküyor. Ancak sınıfın deklarasyonuna bakarsanız abstract sözcüğü dikkat çekiyor. Bir sınıfın abstract olduğunu belirtmek için bu sözcüğü eklersiniz. Sınıf içerisinde ayrıca fieldlar, getter ve setterlar görmektesiniz. Ayrıca banka ekibinin özel ihtiyaç olarak belirttiği özelliği changeBalance(double amount) methodunda kodlanmış olarak görürüz.

TestClass içerisinde new AbstractAccount() ifadesi ile bir değişken yaratmak istediğinizde, hata alırsınız. Bu hata tıpkı interface konusunda karşılaştığımız durum gibidir. Bu hata özetle bize bir abstract class üzerinden bir instance oluşturamayacağımızı söyler. O halde görürüz ki abstract sınıflar, interfaceler ile hem benzeşir, hem ayrışır.

Biraz daha detaylara inelim. Interfaceler için implementasyonlar yazmıştık. Bu implementasyonlar bağlı oldukları interfaceleri sınıf tanımlarında implements sözcüğü ile taşıyorlardı. Ancak bu durum abstract sınıflar için geçerli değildir. Davranış açısından abstract sınıflar normal sınıflara benzerlik gösterirler. Yani abstract sınıflar implements sözcüğü ile değil, extends sözcüğü ile bağlantı kurulacak yapılardır. Ayrıca fieldlar da görürüz ki, bunlar ile iinterfaceler konusunda karşılaşmamıştık. O halde banka ekibimizin iki ayrı hesap türü üzerinde anlaşmaya vardıklarını düşünelim. Bu hesap türlerini ise SuperAccount ve MegaAccount olarak belirlemiş olsunlar. Yukarıda paylaşmış olduğumu abstract sınıf, bu iki hesap türünün nasıl olacağının genel bir şablonunu çizmektedir.

public class SuperAccount extends AbstractAccount {

}

public class MegaAccount extends AbstractAccount {

}

Boş sınıflarımızı bu şekilde oluşturduktan sonra, TestClass içerisinde tekrar durumun testini yapalım.

public class TestClass {

	public static void main(String[] args) {
		
		SuperAccount superAccount = new SuperAccount();
		MegaAccount megaAccount = new MegaAccount();
		
		superAccount.changeBalance(10);
		megaAccount.changeBalance(-10);
		
	}
}
Çıktı:
AbstractAccount.changeBalance()
AbstractAccount.changeBalance()

Görüldüğü gibi abstract sınıf içerisinde bulunan method(lar), alt sınıflar tarafından çağırılabilmektedir. Az önce bir instance yaratamadığımız AbstractAccount sınıfının içerisindeki bir methodun tetiklenebilir hale geldiğini görüyoruz. Akılda tutulması ve hatırlanması gereken önemli bilgi extends sözcüğünün ardından tek bir sınıf gelebilmesidir. Yani implements ile birden fazla arayüzü kullanabilirken, extends ile birden fazla abstract sınıfı kullanamayız.

Şimdi abstract sözcüğünün farklı bir kullanımına odaklanmak istiyorum. Bu sözcük sadece sınıf tanımlamalarında kullanılmamaktadır. Methodlar da aynı zamanda abstract olabilir. Bildiğimiz üzere interface yapıları, içerisinde, method imzaları barındırmaktaydı. Bu durumu abstract sınıflar içerisinde de, abstract methodlar ile sağlamak mümkündür. Bu örneğimiz için bir durum yaratalım.

Varsayalım ki her hesap türü için hesap dokümünün basımını almak zorunlu olsun. Ancak basımın nasıl alınacağını her hesap türü kendisi belirlesin. Yani hem hesapları temsil eden java sınıflarında bir method istiyoruz, hem bu methodunu içinin sınıfa özel doldurulmasını istiyoruz, hem de üst sınıftan bu durumu alt sınıflar için zorunlu kılmasını istiyoruz. Bunu yapabilmek için abstract sınıfımıza, abstract bir method ekleyeceğiz.

public abstract class AbstractAccount {

	//...	
	public abstract void print();
	//...
}

Abstract bir method oluşturmak için, tıpkı abstract sınıflarda yaptığımız gibi, method tanımına abstract sözcüğünü ekleriz. Bu değişikliği yapmak SuperAccount ve MegaAccount sınıflarının hata vermesine sebep olur. Bu hatanın sebebi print methodunun bu sınıflarda override edilmemesidir. Bu eksikliği uygun şekilde giderelim ve main methodumuzu uygun şekilde düzenleyelim.

public class SuperAccount extends AbstractAccount {

	@Override
	public void print() {
		System.out.println("SuperAccount.print()");
	}

}

public class MegaAccount extends AbstractAccount {

	@Override
	public void print() {
		System.out.println("MegaAccount.print()");		
	}

}

public class TestClass {

	public static void main(String[] args) {
		
		SuperAccount superAccount = new SuperAccount();
		MegaAccount megaAccount = new MegaAccount();
		
		superAccount.changeBalance(10);
		megaAccount.changeBalance(-10);
		
		superAccount.print();
		megaAccount.print();
	}
}
Çıktı:
AbstractAccount.changeBalance()
AbstractAccount.changeBalance()
SuperAccount.print()
MegaAccount.print()

Görüldüğü gibi interfacelerin sunduğu fonksiyonaliteyi abstract methodlarla da sağlamak mümkün. Ancak bir sınıf birden fazla interface implementasyonu olabilirken, birden fazla sınıfı tek seferde extend edemez. Bu fark zaman zaman iş görüşmelerinde de sorulmaktadır. Her ne kadar java 8 ile birlikte interface içerisine gövdeli methodlar yazabiliyor olsak da, sınıfların birden fazla arayüzü implemente edebiliyor olması ile interface ve abstract sınıflar birbirinden ayrılmaktadır.

1 comment on Abstract Yapılar

Leave a Reply

Your email address will not be published. Required fields are marked *