Karar Verme – II

Bir önceki yazımızla birlikte javada karar verme konusuna giriş yapmıştık. Öncelikle if ifadesinin tek başına kullanılmasına değinmiştik. Örneğimizde kartta para varsa kulaklık satın alma işlemini gerçekleştirmiştik. Bu örneğin ardından kartta para varsa kulaklığı al ve kartta para yoksa ekrana “Bakiye yetersiz” yazdıran bir if-else örneği yapmıştık.

Ancak bazen öyle durumlar olur ki, karar verme anında ikiden fazla seçeneğimiz olur. Üzerinde uğraştığımız kredi kartları örneğine odaklanarak şöyle bir durum düşünebiliriz; Bir kartın tipi gold ise o karta 4 taksit uygulanır, kartın tipi normal ise o karta 2 taksit uygulanır ve kart tipi bunlardan farklı ise o karta taksit uygulanmaz. Bu daha uzun bir zincir de olabilirdi. Örneğin platium karta n taksit, silver karta t taksit olabilirdi. Bütün bu durumları çok sayıda if-else bloğu ile halletmek makul olmayacaktır.

Javada bu konu da çözüme kavuşturulmuştur. Bu yazıda ilk olarak bunu inceleyeceğiz. Öncelikle mevcut uygulamamızda bir takım değişiklikler yaparak main methodumuzu daha temiz bir hale getirelim. Yazdığımız kodu kaybetmemek için, main methodumuzun içerisindeki kodu, yeni, ayrı bir kartOrnegi() methodumuza alıyoruz. Bu methodu main içerisine commentli bir şekilde ekleyeceğiz. Methodu çalıştırmak istediğimizde, method çağrısını un-comment etmek yeterli olacaktır.

public class TestClass {

	public static void main(String[] args) {
		
		// İlk örneğin çalışması için method çağrısının 
		// önündeki comment işaretini (//) kaldırınız.
		
		//kartOrnegi();

	}

	private static void kulaklikSatinAl(KrediKarti kart) {
		
		if(kart.getBakiye() >= 750) {
			System.out.println("Kulaklık satıldı");
			int yeniBakiye = kart.getBakiye() - 750;
			kart.setBakiye(yeniBakiye);
		} else {
			System.out.println("Bakiye yetersiz");
		}
		
	}
	
	private static void kartOrnegi() {
		
		KrediKarti normalKart = new KrediKarti();
		normalKart.setKartTipi("normal");
		normalKart.setBakiye(1000);
		
		KrediKarti goldKart = new KrediKarti();
		goldKart.setKartTipi("gold");
		goldKart.setBakiye(10000);

		System.out.println("Normal kart önceki bakiye:" + normalKart.getBakiye());
		kulaklikSatinAl(normalKart);
		System.out.println("Normal kart sonraki bakiye:" + normalKart.getBakiye());
		
		System.out.println("Gold kart önceki bakiye:" + goldKart.getBakiye());
		kulaklikSatinAl(goldKart);
		System.out.println("Gold kart sonraki bakiye:" + goldKart.getBakiye());
		
		System.out.println("Normal kart önceki bakiye:" + normalKart.getBakiye());
		kulaklikSatinAl(normalKart);
		System.out.println("Normal kart sonraki bakiye:" + normalKart.getBakiye());
		
	}
	
}

Yukarıda yaptığımız işlem tamamen çalışma alanımızı temiz tutmak içindi. Şimdi de ikinci örneğimiz için yeni bir method kodlayacağız. Bu methodumuzun ismini kartOrnegiCokluSecenek() olarak belirliyoruz. Bu method içerisinde KrediKarti sınıfından 4 tane instance oluşturacağız. İsimlerini ise sırasıyla platiniumKart, silverKart, bronzeKart ve normalKart koyuyoruz. Bu nesnelerin kartTipi fieldlarını da kart ismine uygun olacak şekilde setleyiniz.

Farklı kartlar için bankanın bir kampanya yürüttüğünü ve kart tipine göre taksit yapıldığını düşünelim. Örneğin platinium için 8 taksit, silver için 6 taksit, bronze için 4 taksit uygulansın. Normal kartlarda ise taksit uygulaması olmasın. Banka geriye kalan tüm kart tipleri için ise 2 taksit uygulasın.

Bir başka methodumuz ise kendisine gelen kartın tipine bakıp, kaç taksit yapıldığını geriye dönsün. Bu methodumuzun ismi ise getTaksitSayisi() olsun. TestClass içerisine bu methodları kodlayacağız. İlk adım olarak getTaksitSayisi() methodumuzun boş olacağını belirteyim. Bu method tahmin edeceğiniz üzere parametre olarak bir KrediKarti nesnesi alacaktır.

	private static void kartOrnegiCokluSecenek() {
		
		KrediKarti platiniumKart = new KrediKarti();
		platiniumKart.setKartTipi("platinium");
		
		KrediKarti silverKart = new KrediKarti();
		silverKart.setKartTipi("silver");
		
		KrediKarti bronzeKart = new KrediKarti();
		bronzeKart.setKartTipi("bronze");
		
		KrediKarti normalKart = new KrediKarti();
		normalKart.setKartTipi("normal");
		
	}
	
	private static int getTaksitSayisi(KrediKarti kart) {
		//Derleme hatasını engellemek için 
		// geçici bir return değeri kullanıyoruz
		return -1;
	}

Bu noktada karar verme aşamasında kullandığımız if ve if-else bloklarının dışında yeni bir yapı kullanacağız. Bu yapımıza else-if yapısı ismini veriyoruz. Yukarıda bahsettiğimiz banka kampanyasının kodlamasını da bu yapıyı kullanarak yapacağız. Taksit sayısını get eden methodumuzu aşağıdaki gibi düzenliyoruz.

	private static int getTaksitSayisi(KrediKarti kart) {
		
		String kartTipi = kart.getKartTipi();
		
		if (kartTipi.equals("platinium")) {
			return 8;
		} else if(kartTipi.equals("silver")) {
			return 6;
		} else if(kartTipi.equals("bronze")) {
			return 4;
		} else if(kartTipi.equals("normal")) {
			return 1;
		} else {
			return 2;
		}
		
	}

Kodu biraz incelersek, yeni bir ifade ile karşılaşırız. Bu ifade else if ifadesidir. İfadeyi kullanabilmek için bir if ifadesi öncelikle bulunmalıdır. Bu ifadenin görevi, ikiden fazla seçenek olduğunda bunu işleme tek seferde alabilecek bir yapıyı oluşturmaktır.

Burada bir de minik bir detay daha eklemek istiyorum. Dikkat ettiyseniz String ifadeleri kıyaslarken == operatörünü kullanmadım. Bunu sonraki konularda ele alacağız. Ancak String ifadeleri kıyaslarken, geriye true ya da false değer dönen equals() methodunu kullanmanız gerektiğini, aklınızın bir köşesine yazınız.

Nihayetinde bankanın kampanyasının kodlamaya dökülmüş hali bu şekildedir. Şimdi bunu test etme zamanı. kartOrnegiCokluSecenek() methodumuzu uygun şekilde düzenliyoruz.

	private static void kartOrnegiCokluSecenek() {
		
		KrediKarti platiniumKart = new KrediKarti();
		platiniumKart.setKartTipi("platinium");
		
		KrediKarti silverKart = new KrediKarti();
		silverKart.setKartTipi("silver");
		
		KrediKarti bronzeKart = new KrediKarti();
		bronzeKart.setKartTipi("bronze");
		
		KrediKarti normalKart = new KrediKarti();
		normalKart.setKartTipi("normal");
		
		System.out.println("=== Taksit sayıları ===");
		System.out.println("platinium:" + getTaksitSayisi(platiniumKart));
		System.out.println("silver:" + getTaksitSayisi(silverKart));
		System.out.println("bronze:" + getTaksitSayisi(bronzeKart));
		System.out.println("normal:" + getTaksitSayisi(normalKart));
		
	}

Bununla birlikte bu methodu main methodumuzdan çağırılacak şekilde kodumuzu ayarlıyoruz. Ardından ise main methodumuzu çalıştıracağız.

	public static void main(String[] args) {
		
		// İlk örneğin çalışması için method çağrısının 
		// önündeki comment işaretini (//) kaldırınız.
		
		//kartOrnegi();

		kartOrnegiCokluSecenek();
	}
Çıktı:
=== Taksit sayıları ===
platinium:8
silver:6
bronze:4
normal:1

Bir ufak pratik olarak kartTipi gold olan bir nesne daha yaratınız ve yukarıda yaptığımız gibi ekrana taksit sayısını yazdırınız. Gold kart olarak ekranda 2 taksit olduğunu göreceksiniz.

Yukarıda yaptığımız örneği java alternatif bir yöntem ile daha yapmamıza izin verir. Bu yöntem switch-case yöntemidir. Örneği hiç değiştirmeden yeni bir method ile bunu da inceleyelim. Öncelikle sözdizime değinmek istiyorum. Bir switch case ifadesi kabaca şu şekilde oluşur:

switch (anahtar) {
	case alternatifDeğer:
		//Çalışacak kod
		break;
	case alternatifDeğer2:
		//Çalışacak kod
		break;
	//...
	//diğer case ifadeleri
	//...
	default:
		//Çalışacak kod
		break;
}

Dikkat ederseniz çok sayıda farklı durumu switch-case ile yönetebiliyoruz. Ancak sözdiziminde bir takım detaylar ilgi çekmekte. Bunun sebeplerine gelirsek;

  • Öncelikle switch ifadesi çalışır ve bir değer üretir.
  • Üretilen bu değer her case ifadesi ile karşılaştırılır.
  • Eğer bir eşleşme varsa o blok çalıştırılır.
  • Eğer bir eşleşme yoksa default içerisinde yazılmış kod çalışır.
  • Bir kod çalıştıktan sonra break ile switch akışı kırılır ve ifadenin çalışması sonlandırılır.

Şimdi de taksit sayılarını ekrana yazdıran bir method daha yazalım. Bu method içerisinde birlikte switch-case kullanalım. Methodumuzun ismi ise getTaksitSayisiSwitchCase() olsun. Öncelikle bu methodu tek başınıza kodlamaya çalışın. Sonra aşağıdaki kod ile karşılaştırın.

	private static void getTaksitSayisiSwitchCase(KrediKarti kart) {

		String kartTipi = kart.getKartTipi();

		switch (kartTipi) {
		case "platinium":
			System.out.println("Platinium kart için taksit sayısı:" + 8);
			break;
			
		case "silver":
			System.out.println("Silver kart için taksit sayısı:" + 6);
			break;
			
		case "bronze":
			System.out.println("Bronze kart için taksit sayısı:" + 4);
			break;
			
		case "normal":
			System.out.println("Normal kart için taksit sayısı:" + 1);
			break;

		default:
			System.out.println("Diğer kart için taksit sayısı:" + 2);
			break;
		}
	}

kartOrnegiCokluSecenek() methodumuzun içerisinde bulunan nesneler ile bu methodu test edelim ve çıktıyı else-if yapısındaki çıktıyla karşılaştıralım. Methodumuza şu satırları ekliyoruz ve ardından uygulamamızı çalıştırıyoruz.

		System.out.println("=== Taksit sayıları / switch-case ===");
		
		getTaksitSayisiSwitchCase(platiniumKart);
		getTaksitSayisiSwitchCase(silverKart);
		getTaksitSayisiSwitchCase(bronzeKart);
		getTaksitSayisiSwitchCase(normalKart);
Çıktı:
=== Taksit sayıları ===
platinium:8
silver:6
bronze:4
normal:1
=== Taksit sayıları / switch-case ===
platinium:8
silver:6
bronze:4
normal:1

Javanın temel karar verme ifadelerini son iki yazımız ile inceledik. Umarım faydalı olmuştur. Yazı içerisinde bazı ufak detaylara da dikkat çekmek istedim. Örneğin bir method geriye değer dönerken diğeri dönmüyor. Bu durumda ekran yazdırmak için farklı yaklaşımlar sergiledik. Siz iki methodu da değer dönen veya dönmeyen hale getirip pratik yapabilirsiniz. Ayrıca String ifadelerin nasıl kıyaslanacağına değindik. Üzerinde çalıştığımız örnekleri sınırlı sayıda tuttuk, ancak, bu örnekleri daha da uzatabileceğimizi gözlemledik. Editörümüzde şu ana kadar 4 ayrı proje oluşturduk. Bunlar zamanla daha da artacaktır. Siz bütün bu projeler dışında yeni bir proje oluşturup, farklı örnekler yapıp, tüm öğrendiklerinizi bir arada kullanmaya çalışın. Unutmayın, pratik mükemmelleştirir. Sonraki yazıda görüşmek üzere.

Karar Verme

Karar verme, belirli bir olgunun gerçekleşmesi veya gerçekleşmemesi durumunda, elimizdeki seçeneklerden hangisinin kullanılabilir olduğunun tayin edilmesidir. Karar verme eylemi, akış şemalarımızda sıklık ile karşımıza çıkmaktadır. Programlamanın temellerinden olan bu durumu, javada da karşılayabilmekteyiz.

Bu yazımızda kullanmak üzere, içerisinde TestClass’ı bulunan bir proje oluşturuyoruz ve ismini 04 – DecisionMaking olarak belirliyoruz. Bu konuyu işlerken yine gerçek dünyadan bir örnekle ilerleyeceğiz. Uygulamamızın içerisinde KrediKarti isminde yeni bir sınıf yaratıyoruz. Bu sınıfımız iki field barındırmalıdır. Fieldlardan bir tanesi String değer taşıyan kartTipi diğeri ise sayısal bir değer tutan bakiyedir. Bu alanların erişim düzenleyicileri ise private olmalı ve alanlara getter/setter vasıtasıyla ulaşılmalıdır. Sınıfı öncelikle kendiniz yazıp, sonrasında aşağıdaki kod ile karşılaştırınız.

public class KrediKarti {
	
	private String kartTipi;
	
	private int bakiye;

	public String getKartTipi() {
		return kartTipi;
	}

	public void setKartTipi(String kartTipi) {
		this.kartTipi = kartTipi;
	}

	public int getBakiye() {
		return bakiye;
	}

	public void setBakiye(int bakiye) {
		this.bakiye = bakiye;
	}

}

Şimdi main methodumuza gidiyoruz ve iki ayrı değişken yaratıyoruz. İki değişkenimizin de tipi KrediKarti olup; değişkenlerden bir tanesinin ismi normalKart ve diğeri ise goldKart olacaktır. KrediKarti sınıfına ait bu instanceların, kendi içlerindeki kartTipi alanları sırasıyla “normal” ve “gold” olarak setlenmeli, bakiyeleri ise 1000 ve 10000 olmalıdır. Kodunuzu kendiniz yazdıktan sonra yine aşağıdaki blokla karşılaştırınız.

public class TestClass {

	public static void main(String[] args) {

		KrediKarti normalKart = new KrediKarti();
		normalKart.setKartTipi("normal");
		normalKart.setBakiye(1000);
		
		KrediKarti goldKart = new KrediKarti();
		goldKart.setKartTipi("gold");
		goldKart.setBakiye(10000);
		
	}
	
}

Bu kısa kodlama seansından sonra bu iki obje ile neler yapabileceğimizi düşünelim. Öncelikle bunlar bakiyeleri olan kartlar olduğu için, bu bakiyeleri nasıl karar verme noktası olarak kullanacağımızı hayal edebiliriz. Bu kartlar elinizde olsaydı ve değeri 750 birim olan bir kulaklık almak isteseydiniz, her iki kartın da bunu sağlıyor olması gerekirdi. Ancak normalKart bu kulaklığı bir kere alabiliyorken goldKart burada size farklı bir sonuç sağlayacaktır. O halde kart bakiyesini karar verme noktası olarak kullanabiliriz. Gelin kulaklık satma işlevini gerçekleştiren ve parametre olarak KrediKarti tipinde bir input alan methodu, test sınıfımıza ekleyelim. Bu methoda gönderilen kart objesinin bakiyesi 750 birimi karşılayabiliyorsa, konsola çıktı olarak “Kulaklık satıldı” yazsın ve hemen ardından karttan 750 birim bakiye eksiltsin. Methodumuz şu şekilde olacaktır.

	private static void kulaklikSatinAl(KrediKarti kart) {
		
		if(kart.getBakiye() >= 750) {
			System.out.println("Kulaklık satıldı");
			int yeniBakiye = kart.getBakiye() - 750;
			kart.setBakiye(yeniBakiye);
		}
		
	}

Öncelikle şu ana kadar yaptığımız örnekler arasında, programlamaya gerçekten en yakın durduğumuz örneğin bu olduğunu belirtmek isterim. Yukarıdaki method her ne kadar böyle bir özellik taşısa da, normal şartlarda tasarımını doğru bulacağım bir kodu temsil etmiyor. Unutmayın ki, bu yazıların amacı belli başlı kalıpları öğretmektir. Dolayısı ile örneklerdeki hedefimiz doğru tasarım yapmak değil, konuyu benimsetmektir. Eğer bir miktar programlama bilginizle bu yazıları okuyorsanız ve size yanlış gelen şeyler varsa, lütfen bu bahsettiğim önceliği gözden kaçırmayınız.

Bu kısa bilgiden sonra methodu incelemeye alalım. Method henüz değinmediğimiz bir sebeple static anahtar sözcüğünü imzasında taşımaktadır. Bu noktaya takılmadan devam edelim. Methodumuz TestClass sınıfımızda bulunan, main methodundan sonraki ikinci methoddur. Methodların sınıf içerisinde satır numarası olarak yerleri önemli değildir. Dolayısı ile buradaki ikinci ifadesi sizi yanıltmasın. Methodumuzun dönüş tipi voiddir. Yani geriye bir bilgi dönmez. Methodumuz içerisine KrediKarti tipinde bir parametre alır. Bu durumu kartı karşınızdaki kişiye vermek gibi düşünebilirsiniz.

Methodun içerisine baktığımızda ise bir if bloğu içerisinde kulaklığın satıldığını konsola yazdıran ve hemen ardından kart bakiyesinden 750 birim düşmekte olan bir kod görürüz. Buradaki yöntemde, mevcut bakiye get edilmiş, get edilen değerden 750 birim çıkarılmış ve yeniBakiye ismindeki bir int değişkene atanmıştır. Sonrasında bu int değişken ile güncel bakiye setlenmiştir.

Gelelim bu kodun karar verdiğimiz kısmına. buradaki if ile başlayıp hemen ardından parantezle devam eden kısım bizim karar noktamızdır. Burada öncelikle parantez içerisindeki ifadeye odaklanalım.

if(kart.getBakiye() >= 750)
Karar noktamız

Operatörler yazımızdan da hatırlayacağınız üzere >= ifadesi bir operatördür. Bu operatör sağında ve solunda bulunan ifadeleri kıyaslar. Eğer soldaki ifade sağdakinden büyükse veya eşit ise geriye true döner. Bu durumda ifademizi şu şekilde okuyabiliriz : if(condition), yani konuşma diline yaklaşmak istersek “Eğer şartımız doğru ise” şeklinde belirtebiliriz. Bu durumun bir sonucu olarak, eğer şart doğru ise if bloğundaki kod çalışır. Yani bu methoda gönderilen kartın bakiyesi 750’den büyük veya eşit ise blok çalışır. Gelin bunu birlikte deneyimleyelim. TestClass’ın durumu test etmek üzere düzenlenmiş son halini aşağıya yapıştırıyorum ve ardından çalıştırıp aldığım çıktısını ekliyorum.

public class TestClass {

	public static void main(String[] args) {

		KrediKarti normalKart = new KrediKarti();
		normalKart.setKartTipi("normal");
		normalKart.setBakiye(1000);
		
		KrediKarti goldKart = new KrediKarti();
		goldKart.setKartTipi("gold");
		goldKart.setBakiye(10000);

		System.out.println("Normal kart önceki bakiye:" + normalKart.getBakiye());
		kulaklikSatinAl(normalKart);
		System.out.println("Normal kart sonraki bakiye:" + normalKart.getBakiye());
		
		System.out.println("Gold kart önceki bakiye:" + goldKart.getBakiye());
		kulaklikSatinAl(goldKart);
		System.out.println("Gold kart sonraki bakiye:" + goldKart.getBakiye());
		
	}

	private static void kulaklikSatinAl(KrediKarti kart) {
		
		if(kart.getBakiye() >= 750) {
			System.out.println("Kulaklık satıldı");
			int yeniBakiye = kart.getBakiye() - 750;
			kart.setBakiye(yeniBakiye);
		}
		
	}
	
}
Çıktı:
Normal kart önceki bakiye:1000
Kulaklık satıldı
Normal kart sonraki bakiye:250
Gold kart önceki bakiye:10000
Kulaklık satıldı
Gold kart sonraki bakiye:9250

Gördüğünüz gibi bakiye başarılı şekilde düşüyor ve kulaklığın alındığına dair ibareleri görüyoruz. Peki normalKart ile ikinci bir satın alma işlemi daha yapmak isteseydik, yani bakiyemiz olmasaydı, neler olurdu? Tahmin edebildiniz mi? main methoduna aşağıdaki kod parçasını ekleyip kodu tekrar çalıştırdığımızda çıktı şu şekilde olacaktır.

		System.out.println("Normal kart önceki bakiye:" + normalKart.getBakiye());
		kulaklikSatinAl(normalKart);
		System.out.println("Normal kart sonraki bakiye:" + normalKart.getBakiye());
Çıktı:
Normal kart önceki bakiye:1000
Kulaklık satıldı
Normal kart sonraki bakiye:250
Gold kart önceki bakiye:10000
Kulaklık satıldı
Gold kart sonraki bakiye:9250
Normal kart önceki bakiye:250
Normal kart sonraki bakiye:250

Görüleceği üzere kulaklığın satıldığına dair bir ifade olmadan önceki ve sonraki bakiye ekrana basılmış. Bu da if şartının çalıştığını, bunun sonucunda false değer geldiği için bloğun kendisinin çalıştırılmadığını göstermektedir. Peki biz bunu da belirtmek istesek, yani eğer bakiye yoksa “Bakiye yetersiz” yazdırmak istesek ne yapardık? Bir if bloğu daha eklemek iş görecektir. Ancak bu durumda mantıksal bir problem oluşur. Bu iki şartın bir arada bulunma olasılığı yok. Yani hem bakiye olmaması hem de kulaklığı almanız olası değil. Dolayısı ile iki işi ayrı ayrı yapmak biraz verimsiz gözüküyor. Yani şartlardan biri çalışırsa, diğerinin çalışmaması makul gözüküyor. İşte bu durumu javada çözebiliyoruz. if ifadesinin kardeşi olan ve birlikte kullanılan else ifadesi bunu bize sağlamaktadır. if-else kullanımını şu şekilde temsil edebiliriz:

if(şart) { 
	//şart doğru ise çalışır
} else { 
	//şart doğru değil ise çalışır
}

Dikkat ederseniz else ifadesinin yanında bir parantez bulunmuyor. Dolayısı ile if koşulunun başarısız olmasının, else bloğundaki kodun çalışması için yeterli olduğunu söyleyebiliriz. O halde bunu deneyip görelim. kulaklikSatinAl methodumuzun içeriğini şu şekilde düzenleyelim.

	if(kart.getBakiye() >= 750) {
		System.out.println("Kulaklık satıldı");
		int yeniBakiye = kart.getBakiye() - 750;
		kart.setBakiye(yeniBakiye);
	} else {
		System.out.println("Bakiye yetersiz");
	}

Hemen ardında programı tekrar çalıştırıp çıktısına bakalım.

Çıktı:
Normal kart önceki bakiye:1000
Kulaklık satıldı
Normal kart sonraki bakiye:250
Gold kart önceki bakiye:10000
Kulaklık satıldı
Gold kart sonraki bakiye:9250
Normal kart önceki bakiye:250
Bakiye yetersiz
Normal kart sonraki bakiye:250

Az öncekinden farklı olarak “Bakiye yetersiz” ifadesini konsolda göreceğiz. Böylece iki ayrı if yazmadan bu durumu nasıl yöneteceğimizi görmüş olduk.

Bir sonraki yazımızda da karar verme konusuna devam edeceğiz. Bu noktadan sonra işler tamamen ciddileşmiş gözüküyor. Daha evvel önerdiğim gibi, geçmiş konuları zaman zaman tekrar etmeniz size faydalı olacaktır. Yazılarda mümkün olduğunca önceki konulara referans vermeye çalışıyorum. Eğer bu referansları çok sık kontrol etme ihtiyacı duyuyorsanız tekrar şart demektir. Bir sonraki yazıda görüşmek üzere!