Java Günlüğüm
Yazılım, Java, BT, azıcık felsefe, biraz mizah...
  • Udemy Eğitimleri
  • Temiz Kod
  • Tasarım Kalıpları
  • Hakkımda
  • Arşiv
RSS
06 Mayıs 2014

Java Kodunuzun Nesne-Merkezli Olmadığının 10 İşareti – III: Yüksek İçerik Bağımlılığı

Akin Java

Geliştirdiğimiz Java kodunun nesne-merkezli olmadığının 10 işaretinin neler olduğunu bu yazıda listelemiş ve bu yazıda da ilk madde olarak sınıflardaki aşırı statik metot kullanımını ele almıştık. Şimdi de ikinci maddeyi ele alalım:

Yüksek içerik bağımlılığı (high content coupling).

Malum bağımlılık (coupling), bir yazılım elemanının, diğer elemanlar hakkındaki bilgisinin derecesidir. Arzu edilen, mümkün olan en az derece bağımlılığa sahip yapılar kurgulamaktır. İster framework kurgulayın, isterse basit bir iş yapan metot kurgulayın, hem karmaşıklığı yönetmek hem de daha rahat değişebilen yapılar elde edebilmek için daima bağımlılığı az yazılım elemanları ile yazılım geliştirtirin.

Yazılım mühendisliği ya da “iyi kod nasıl yazılır” cinsinden kitaplar bağımlılık konusunu etraflıca ele alırlar ve farklı bağımlılık şekillerini incelerler. Örneğin wikipedia buna güzel bir görsel ile yer ayırmıştır. Bu anlamda içerik bağımlılığı, en kötü bağımlılıktır. Çünkü bir yazılım elemanı, bir başka yazılım elemanının iç yapısına bağımlıdır. Sınıf seviyesinde ele alındığında içerik bağımlılığı, bir sınıfın, bir başka sınıfın iç yapısına bağımlı olması demektir. (Bundan sonra bağlamımız sınıf/arayüz gibi tip seviyesi olacaktır.) Bu durumda bir sınıfı anlamak için bağımlı olduğu sınıf(lar)ı anlamak gereklidir. Benzer şekilde böyle bir sınıfı değiştirmek için bağımlı olduğu sınıf(lar)ı da değiştirmek gerekebilecektir. Bunlar ise karmaşıklığı arttıran ve değişimi zorlaştıran dolayısıyla da maliyeti arttıran, istenilmeyen durumlardır. Bu yüzden bu tür bağımlılığa “pathological coupling” yani “hastalıklı bağımlılık” da denir. 

Yüksek bağımlılık ve düşük bağımlılık.

Bağımlılığı sıfır olan bir sınıftan bahsedemeyiz. Sadece bir sınıfımız varsa, evet bağımlılığı sıfırdır. Yani no coupling! Birbirine bağımlılığı olmayan, bölük pörçük oluşturulmuş sınıflar da bir sistem oluşturmayacağından, bir sistemin sınıfları kendi aralarında bir şekilde bir bağımlılığa sahip olmak zorundadırlar. Bu noktada bağımlılığın keyfiyeti önemlidir. İçerik bağımlılığı, bir sınıf, bir başka sınıfın iç yapılarına ulaşmasıdır dedik. Yani bir sınıfın üye değişkenleri (sınıf ve nesne değişkenleri) dışarıdan erişilebiliyorsa, içerik bağımlılığı oluşur. Bu anlamda bir başka sınıfın üye değişkenlerine erişmek ya da onları değiştirmek, içerik bağımlılığıdır. Bir sınıfın üye değişkenleri dış erişime karşı korunmuyorsa, diğer sınıflar sıklıkla bu değişkenlere ulaşıyorlar demektir. Bu durumda o sınıf hizmet yerine veri sağlıyor demektir. Yani diğer sınıflar, söz konusu sınıf üzerinde metot çağrısı yapmak yerine veri alış verişi yapıyorlar demektir. Örneğin aşağıdaki Date sınıfı bu türden bir sınıftır:

public class Date {
	
	private int day;
	private String month;
	private int year;

	public Date(int day, String month, int year) {
		this.day = day;
		this.month = month;
		this.year = year;
	}

	public int getDay() {
		return day;
	}

	public void setDay(int day) {
		this.day = day;
	}

	public String getMonth() {
		return month;
	}

	public void setMonth(String month) {
		this.month = month;
	}

	public int getYear() {
		return year;
	}

	public void setYear(int year) {
		this.year = year;
	}

	public String toString() {
		return "Date: " + day + " - " + month + " - " + year;
	}
}

Pek çok programcı, Date sınıfının nesne-merkezli olduğunu iddia edecektir. Çünkü sarmalama (encapsualtion) kurallarına uyulmuş, yani alanlar private yapılmış ve doğrudan erişim engellenmiştir. Aslında yukarıdaki sınıf, tarih ile ilgili sadece veri taşımakta bundan dolayı da diğer sınıflarla arasında oluşacak olan bağımlılığin seviyesi içerik olarak kalacaktır. Yani Date sınıfı hizmet değil, veri sağlayacaktır. Bundan dolayı örneğin bir Date nesnesine sahip olup da ertesi günü elde etmek isteyen bir başka nesne, ya da Date nesnesini farklı formatlarda basmak isteyen bir diğer nesne, Date nesnesinden get metotlarıyla bilgi alacak ve bu fonksiyonlari kendileri yerine getireceklerdir. Yani örneğin “ertesi gün” hizmeti Client1 nesnesi tarafından şöyle geliştirilmiş olabilir:

/**
 * Client1 is the class that calculates next day of a given Date.
 * 
 * @author akin
 * 
 */
public class Client1 {

	public Date getNextDay(Date date) {

		int day = date.getDay();
		String month = date.getMonth();
		int year = date.getYear();

		// First check wrong days and months
		if (day > 31) {
			System.out.println("Wrong date entered: " + date);
			System.exit(1);
		}

		if (year < 0) {
			System.out.println("Wrong date entered: " + date);
			System.exit(1);
		}

		if (!isMonthCorrect(month)) {
			System.out.println("Wrong date entered: " + date);
			System.exit(1);
		}

		// February is a special case so handle it separately
		if (month.equalsIgnoreCase("February") || month.equalsIgnoreCase("Subat")) {
			if (day == 28) {
				boolean isLeapYear = isLeapYear(year);
				if (isLeapYear)
					day = 29;
				else {
					day = 1;
					month = "March";
				}
			} else if (day == 29) {
				boolean isLeapYear = isLeapYear(year);
				if (isLeapYear) {
					day = 1;
					month = "March";
				} else {
					System.out.println("Wrong date entered: " + date);
					System.exit(1);
				}
			} else if (day == 30 || day == 31) {
				System.out.println("Wrong date entered: " + date);
				System.exit(1);
			} else {
				day++;
			}
			date.setDay(day);
			date.setMonth(month);

			return date;
		}

		// For other months
		if (day < 30)
			day++;
		else {
			if (day == 30) {
				day = 1;
				if (month.equalsIgnoreCase("April") || month.equalsIgnoreCase("Nisan"))
					month = "May";
				else if (month.equalsIgnoreCase("June") || month.equalsIgnoreCase("Haziran"))
					month = "July";
				else if (month.equalsIgnoreCase("September") || month.equalsIgnoreCase("Eylul"))
					month = "October";
				else if (month.equalsIgnoreCase("November") || month.equalsIgnoreCase("Kasim"))
					month = "December";

			} else if (day == 31) {
				day = 1;
				if (month.equalsIgnoreCase("January") || month.equalsIgnoreCase("Ocak"))
					month = "February";
				else if (month.equalsIgnoreCase("March") || month.equalsIgnoreCase("Mart"))
					month = "April";
				else if (month.equalsIgnoreCase("May") || month.equalsIgnoreCase("Mayis"))
					month = "June";
				else if (month.equalsIgnoreCase("July") || month.equalsIgnoreCase("Temmuz"))
					month = "August";
				else if (month.equalsIgnoreCase("August") || month.equalsIgnoreCase("Agustos"))
					month = "September";
				else if (month.equalsIgnoreCase("October") || month.equalsIgnoreCase("Ekim"))
					month = "November";
				else if (month.equalsIgnoreCase("December") || month.equalsIgnoreCase("Aralik")) {
					month = "January";
					year++;
				}
			}
		}

		date.setDay(day);
		date.setMonth(month);
		date.setYear(year);

		return date;
	}

	private boolean isLeapYear(int year) {
		boolean isLeapYear = false;

		// divisible by 4
		isLeapYear = (year % 4 == 0);

		// divisible by 4 and not 100
		isLeapYear = isLeapYear && (year % 100 != 0);

		// divisible by 4 and not 100 unless divisible by 400
		isLeapYear = isLeapYear || (year % 400 == 0);

		return isLeapYear;
	}

	private boolean isMonthCorrect(String month) {
		boolean isCorrectMonth = false;
		if (month.equalsIgnoreCase("January") || month.equalsIgnoreCase("February") || month.equalsIgnoreCase("March")
				|| month.equalsIgnoreCase("April") || month.equalsIgnoreCase("May") || month.equalsIgnoreCase("June")
				|| month.equalsIgnoreCase("July") || month.equalsIgnoreCase("August")
				|| month.equalsIgnoreCase("September") || month.equalsIgnoreCase("October")
				|| month.equalsIgnoreCase("November") || month.equalsIgnoreCase("December") ||
				month.equalsIgnoreCase("Ocak") || month.equalsIgnoreCase("Subat") || month.equalsIgnoreCase("Mart")
				|| month.equalsIgnoreCase("Nisan") || month.equalsIgnoreCase("Mayis")
				|| month.equalsIgnoreCase("Haziran") || month.equalsIgnoreCase("Temmuz")
				|| month.equalsIgnoreCase("Agustos") || month.equalsIgnoreCase("Eylul")
				|| month.equalsIgnoreCase("Ekim") || month.equalsIgnoreCase("Kasim")
				|| month.equalsIgnoreCase("Aralik"))
			isCorrectMonth = true;

		return isCorrectMonth;
	}
}

Verilen bir Date nesnesini farklı formatlarda basan Client2 sınıfına bir bakalım:

/**
 * Client2 is the class that prints a given Date in different formats.
 * 
 * @author akin
 * 
 */
public class Client2 {

	public void printDate1(Date date) {
		int day = date.getDay();
		String month = date.getMonth();
		int year = date.getYear();
		System.out.println("Date (dd-mm-yyyy): " + day + " - " + month + " - " + year);
	}

	public void printDate2(Date date) {
		int day = date.getDay();
		String month = date.getMonth();
		int year = date.getYear();
		System.out.println("Date (mm-dd-yyyy): " + month + " - " + day + " - " + year);
	}

	public void printDate3(Date date) {
		int day = date.getDay();
		String month = date.getMonth();
		int year = date.getYear();
		System.out.println("Date (dd/mm/yyyy): " + day + " / " + month + " / " + year);
	}
	
	public void printDate4(Date date) {
		int day = date.getDay();
		String month = date.getMonth();
		int year = date.getYear();
		System.out.println("Date (mm/dd/yyyy): " + month + " / " + day + " / " + year);
	}

}

Eğer Client1 ve Client2 gibi bir sınıf bir hizmeti yerine getirmek, bir şeye karar vermek vb. için bir başka sınıfın iç yapılarına ulaşıyor ya da ondan veri alıyorsa, bu sınıfların aralarındaki bağımlılık yüksektir. Bu durumda Client1 ve Client2 sınıflarını anlamak içın Date sınıfını anlamaya, Date sınıfını değiştirdiğinizde de muhtemelen bu iki sınıfı değiştirmeye ihtiyacınız olacak demektir. Bu şekilde bağımlılığı yüksek olan sınıfların birliktelikleri de (cohesion) düşüktür ya da bir başka deyişle, Date sınıfı tarih kavramının hepsini halletmek yerine sadece bir kısmını halletmekte, bundan dolayı da diğer kısımları farklı yerlerde halledilmektedir. Yani tarih ile ilgili veri ve hizmet bir yerde toplanmayıp, pek çok yere dağılmıştır. Bir de birbirinden habersiz programcıların, bu türden Date üzerine hizmet veren metotları farklı yerlerde farklı şekillerde yazdıklarını düşünün. İşte, iyi programcılarla iyi bir yazılım geliştirilmemesinin en temel sebebi budur. İyi futbolculardan iyi bir takım olmamasına karşın, vasat ama iyi organize olmuş oyunculardan oluşmuş takımın daha iyi performans göstermesi gibidir bu durum. İlki program yazan programcılardır, ikincisi ise yazılım geliştiren bir takımdır. Yukarıdaki Date sınıfının içerik bağımlılığından kurtarmanın yolu, bu sınıfın veri taşıyıp dağıtmasının önüne geçmektir. Date ya da bir sınıf veri taşımak için kurgulanmamalıdır, hizmet vermek için kurgulanmalıdır. Yani, Client1 ve Client2‘deki servisler aslında Date sınıfında olmalıdır. Diğer sınıflar da Date‘den veri yerine hizmet almalıdırlar. Böyle bir yapıdaki Date sınıfının arayüzü pekala şöyle bir DateI arayüzü ile temsil edilebilir:

/**
 * DateI represents date related services to be implemented in subclasses. 
 * @author akin
 *
 */
public interface DateI {

	public abstract String toString();

	public abstract String print();

	public abstract String printShort();

	public abstract String printLong();

	public abstract String print(Locale locale);

	public abstract DateI getNextDay();

	public abstract String getWeekDay();

	public abstract String print(Locale locale, DateFormat format);

}

Bağımlılığı sadece arayüz seviyesinde tuttuğumuzda, en sağlıklı bağımlılığa ulaşmış oluruz. Eğer Date sınıfı, DateI arayüzünü implement ederse ya da yerine getirirse, üzerinde sadece hizmet veren metotlar olacak ve veri taşımak yerine hizmet verecektir. DateI‘nin metotlarından bazılarının String döndürdüğüne dikkat edin. Buradaki String taşınan veriden ziyade, hizmet sonucu üretilen nesneyi temsil etmektedir ve kaçılmazdır. Bu anlamda sınıflar arasında gidip gelen veriyi ne kadar az tutarsanız, içerik bağımlılığınız o derece az oalcak ve sisteminizi hem anlamak hem de değiştirmek yani bakımını yapmak o kadar kolay olacaktır. Bu yüzden DateI arayüzünde get/set metotlarının olmadığına dikkat edin. “Getter ve setter metotları şeytandır” diyen Allen Holub‘ı da burada anmakta fayda vardır.

Bu durum GoF tarafından “program to an interface, not an implementation”  yani “arayüze programlama yapın, gerçekleştirmeye değil” şeklinde ifade edilmiştir. Dolayısıyla açık/kapalı prensibi (open/closed principle) olarak bildiğimiz, “genişlemeye açık, değişikliğe kapalı” (open for extension, closed for modification) şeklindeki kurala da uyarsak, DateI‘nin sunduğu arayüz hiç bir zaman değişmeyecek demektir. Bu durumda Date sınıfını kullanan yani odnan hizmet olan hiç bir sınıfın, Date‘deki bir değişiklikten dolayı değişmesi gerekmeyecektir.

Bu yüzden, üç-beş private alanı bir araya getirip, sağ tıkla set/get metotlarını üreterek yapılan şey programlama olabilir ama nesne-merkezli programlama değildir. Bu şekilde program yazılabilir ama o program hiç bir zaman software/yazılım seviyesine çıkmayacaktır.

Amacımız, az bağımlılıklı ve yüksek birliktelikli ya da odaklı (lowly-coupled, highly-cohesive) yazılımlar geliştirmektir. Hemen her türlü yazılım kalitesi temelde bu iki kavram üzerine bina edilir. Bunları göz önüne almadan hangi dili ya da hangi frameworkü kullandığınızın hiç bir önemi yoktur. Ama ben bu türden kodlara o kadar çok ve sık rastlıyorum ki.

Bağımlılığı az, birlikteliği yüksek insan ve programcı olmak ümidiyle…

Toplam görüntülenme sayısı: 1692

7 Bunu beğendim 🙂
Tweet
Follow me
Tweet to @kaldiroglu
20 Nisan 2014

Temiz Kod Nasıl Yazılır? – Basitlik İlkesi – I

Akin Java, Yazılım Mühendisliği Etkin basit java nasıl yazılır?, Etkin Java, Etkin kod

Bu yazıda, daha önce paylaşmış olduğum Temiz ve Kaliteli Kod Nasıl Yazılır? başlıklı yazıda bahsettiğim kaliteli kodun ilkelerinden olan basitliği ele alacağım.

Sanılanın aksine bir şeyi basit tutmak zordur, uzun bir şekilde ele almak çok daha kolaydır. Çünkü bir şeyi ilk defa yaparken büyük bir ihtimalle en uzun şekliyle, en zor haliyle yaparız. Ancak o konuda tecrübe sahibi oldukça, anlayışımız, yetkinliğimiz gelişir ve işin aslına odaklanabiliriz. Basitleştirmek, bir şeye tümüyle, her yönüyle hakim olup, asıl ile teferruatı ayırmak, neyin gerekli neyin gereksiz olduğuna karar vermektir, orasını burasını gelişi güzel budamak değildir. İlkini yapan beğenilir, ikincisini yapan indirgemeci olmakla ya da aşırı basitleştirmekle eleştirilir. Einstein’e atfedilen “her şey olabilidiğince basit olmalı, ama daha da basit değil” (everything should be made as simple as possible, but no simpler.) ya da “her şeyi olabildiğince basitleştir, ama daha da basit yapma”  (make things as simple as possible, but not simpler.) cümleleri aslında bunu ifade eder.

Bir şey ne zaman mükemmele ulaşır? Mükemmellik nasıl elde edilir? Kamaşıklıkla mı yoksa basitlikle mi? Küçüklükle mi büyüklükle mi? Mükemmellik sonuçta insan taktiridir. İnsan ise basiti daha rahat anlar ve taktir eder, karmaşığı anlamak zordur. Eğer aynı iş, bir basit bir de karmaşık yöntemle yapılabiliyorsa, akl-ı selim olan herkes basit olanı seçer. Mükemmellik tabi olarak algıya bağlıdır ama sanırım Fransız yazar Antoine de Saint-Exupery‘ın şu sözü mükemmel olmanın en temel özelliği olan basitliği çok güzel bir şekilde ifade etmektedir: “Mükemmellik, eklenecek bir şey olmadığında değil, çıkarılacak bir şey olmadığında başarılır.” (Perfection is achieved, not when there is nothing more to add, but when there is nothing left to take away.) Dolayısıyla küçük her zaman güzeldir. Küçüklük dışında bir güzelleştirme aramaya gerek yoktur.

Basitleştirmek çok sağlıklı ve sağlam bir anlayış, vukufiyet gerektirir. Bu anlayış ve vukufiyeti kazanmak hem zeka hem bilgi ve tecrübe hem de çalışma gerektirir. (Bu anlamda “basit”liği, ele yüze bulaştırmadan, basit bir şekilde anlatmak o kadar zor ki! Hele benim gibi mükemmelliyetçi birisi için. Özellikle yazılım ve programlama gibi aslında zaten anlaşılması ve tarif edilmesi zor olan soyut yapılar için bir çırpıda basit olmayı anlatmak bence çok daha zor bir iş. En azından benim boyumu aşar. Yani bu yazıyla her türlü karizmayı çizdirme riskini göz önüne almış oluyorum. 🙂 Bu yüzden bu yazıyı önce yayınladım sonra geri çektim, üzerine bayağı düşündüm, yaz-boz yaptım ve eskiden bu yana düşündüğüm ama formal bir hale dökemediğim “basit”likle ilgili biraz daha geliştirdiğim anlayışımı paylaşıyorum.)

Basitliği tarif etmek zor hayattaki pek çok şey gibi ama ters örnekleri gördüğümüzde en azından bu basit değil diyebiliriz. Örneğin şu yazıda bahse konu olan ifade kesinlikle basit değil. Bu ifadenin basit olmayıp son derece karmaşık olmasına sebep olan şey bence iki tanedir: İlk ve en temel sebep, bir yerde birden fazla şey yapmak. Yani bir yerde pek çok şeyi halletmeye çalışmak, odaklanmamaktır. Karmaşıklığın en temel sebebi ise budur. Bu bir içerik problemidir ve genelde problemlerin en kötüsü, üstesinden en zor gelinenidir. İkincisi ise, bir cümlede (statement) birden fazla ifade (expression) kullanılmış olmasıdır. Bu tabiki ilk problemin bir sonucu ve şekilsel bir durum. Bütün bunlara bir de Java’daki “?:” operatöründe çok akışkan olmayan yani çok aşina olmayan bir programcı eklenince, anlamak hakikatten çok zor hale geliyor.

Kodun basitliğine gelirsek, basit yazılan kodun temelde iki özelliği vardır: Anlaşılırdır ve kısadır. Anlaşılır olmak için şekil açısından öncelikle kod standartlarına daima uyulmalıdır. Eğer bir sebepten dolayı uyulamazsa bu durumu sebebiyle birlikte belirtilmelidir ki insanlar şaşırıp burada fazladan zaman harcamasınlar. Dolayısıyla anlaşılır kod,

  • anlaşılır isimler içeren,
  • ferah, mekanı etkin kullanan dolayısıyla rahat okunan,
  • tutarlı yani şaşırtmayan, beklendiği gibi, aşina olunan,
  • açıklanmış (commented) koddur.

Bunlar anlaşılır kodun şekil şartlarıdır. Bu amaçla paylaştığım isimlendirme ve şekil standartlarına buradan ulaşabilirsiniz.

İçerik açısından ele alındığında anlaşılır kod, okuyananın çok zeki ya da konuya çok hakim olmasına gerek bırakmadan kavranan koddur. Martin Fowler, Refactoring isimli güzel kitabında “Herhangi birisi bilgisayarın anlayacağı kod yazabilir. Fakat iyi programcılar insanların anlayabileceği kod yazarlar.” (Any fool can write code that a computer can understand. Good programmers write code that humans can understand.) der. Dolayısıyla programcı öncelikle kendi anlaması için kod yazmalıdır. Kodunu yazdıktan sonra dönüp bir bakmalı, 3 ay ya da 1 yıl sonra bu kodu anlamak için ne kadar enerji harcayacağını düşünmelidir. Bu anlamda soru “bir kod parçasının kendini ne kadar açık bir şekilde okuyucusuna anlattığı”dır?

Anlaşılır olmak belki bir kaç farklı seviyede ele alınmalıdır. Yani bir kodun anlaşılır olması, sadece o kodun anlaşılır bir şekilde yazılmaya çalışılmasıyla elde edilemeyecek kadar geniş ya da çok faktörlü bir problemdir. Çünkü kod bir son çıktıdır ve kendinden önceki ihtiyaç analizi, mimari ve fonksiyonel tasarım gibi çalışmalar sonucunda üretilebilir. Bu anlamda anlaşılırlık, iş (business) seviyesinden başlamalıdır. Yani basitlik en temelde iş seviyesinde ele alınmalıdır. Yapılacak işin ne kadar kısa, öz, tutarlı olduğu o işin koduna da etkir. Karmaşık bir iş kuralını basit yazmak zordur örneğin. Soru öncelikle “o iş kuralı o kadar karmaşık mı olmalıdır”dır. Ya da “yazılım ile otomatikleştirmeye çalıştığımız süreçlerimiz en basit, en kısa ve en anlaşılır halinde midir” sorularının cevaplanması gereklidir. Bu noktada ünlü felsefeci Adorno’nun “yanlış hayat doğru bir şekilde yaşanamaz” (wrong life cannot be lived rightly) sözü akla geliyor. Karmaşık iş yapış şekillerini basit bir halde koda dökmek zordur diyebiliriz yani. Ülkemizde gelişi güzel, parça parça ya da yazılıma geçirme safhasında akla gelip kurgulanan süreçler gibi problemlerden dolayı zaten iş yapış şekillerimiz sağlıklı değildir, çoğu zaman da onu yapan kişiye bağlıdır. Dolayısıyla süreçleri bölük pörçük, örneğin “ekran” ya da “sayfa” düzeyinde ele alınarak analiz edilmeye çalışılmış bir iş alanın iş kurallarını kodunuzda örneğin nasıl yönetebilirsiniz ki?

İş seviyesinden sonra mimari seviyede basitlikten söz etmeliyiz. Mimarinin ne olduğunu biliyoruz; katmanlar (tiers), bileşenler (components), bunların arayüzleri, aralarındaki ilişkiler, protokoller, “kim kiminle konuşacak”, “hangi bileşen neyi yapacak”, “performans, ölçeklenirlik, güvenirlik, güvenlik vb. kriterleri nasıl sağlayacağız” gibisinden sorular, kullanılacak teknolojiler, donanım mimarisi, sistemin topolojisi vs. hep mimarinin konusu. Mimari bu anlamda sistemin yüksek seviyeli bir taslağıdır.

Mimari bileşenlerin ve aralarındaki ilişkilerin ne kadar basit ve anlaşılır oldukları kesinlikle çok önemlidir. Bu noktalarda alınan kararlar açık bir şekilde ne kadar basit kod yazdığınızı etkiler. Daha doğru ifadeyle mimari kararlar kodunuzun ne kadar basit yazılabileceğini doğrudan etkiler. Kötü oluşturulmuş mimari, karmaşık bir mimari, ya da baştan güzel kurgulanan ama sonra özellikle kontrolsüzlükten ve sisteme giren çıkan yeni programcı vs.nin var olan mimariyi özümsemeden, bilinçsiz ve pragmatik olarak yaklaşmalarından dolayı rayından çıkmış bir mimaride kodunuzu ne kadar basit yazabilirsiniz ki? Java EE’nin ya da Spring’in teklif etmiş olduğu mimariler, mimari şablonlar (architectural patterns) aslında uygulandığı taktirde daha rahat bir yapı kurgulamanızı ve nihayetinde daha basit kod yazmanızı sağlayan yaklaşımlar olarak son derece önemlidirler. Bu anlamda Demeter Kanunu (Demeter Law) sadece sınıflar düzeyinde geçerli değildir, bileşenler ve katmanlar düzeyinde de dikkat edilmesi gereken bir prensiptir. Benzer şekilde daha sonra ele alacağımız “odaklılık” konusu tabi olarak mimari için de geçerlidir.

Daha sonra tasarım seviyesinde basitlik ele alınmalıdır. Sınıf, arayüz, metot vb. yapılar ve aralarındaki ililşkiler, basitlik üzerine kurgulanmadıkça basit kod yazamazsınız. Eğer yazılım geliştirme sürecinizde bir tasarım safhası yoksa o zaman hakikatten basit kod yazmanız çok zor. Çünkü tasarım demek kodunuz hakkında düşünmek demektir. Kodunuzu düşünmeden yazdığınızda çoğu zaman kaliteli ve basit kod yazmaya odaklanamazsınız. Kodu doğru yazmaya, iş mantığını doğru bir şekilde yansıtmaya daha fazla odaklanırsınız. İş, mimari ve tasarım seviyesinde ele alınmayan konuların getirdiği bütün yük üzerinizdedir. (Bu yüke technical debt deniyor.) Bunlar o kadar fazla sayıda faktör olarak ortaya çıkarlar ki zaten zihninizin bu kadar çok şeyi aynı anda halletmesi son derece güçtür. Bu durumda ister istemez kötü kod yazarsınız, basit yazayım derken performansı gözden kaçırırsınız mesela. 

Tasarımda yapılacak, sorumlulukları nesnelere atama, nesneler arasındaki ilişkileri düzenleme, değişmesi muhtemel kısımlarla diğer kısımları birbirinden yalıtma, bu amaçlar için tasarım şablonlarını kullanma vb. çalışmalar, kodunuzun kalitesini doğrudan etkiler. Hatta tasarım çalışmaları sonuçta sistemde var olacak her türlü tipin arayüzünü belirlediği için, kod kalitesi büyük oranda tasarımda belirlenir. Bu anlamda bir tasarım çalışması yapmadan kaliteli kod yazmak aslında hayaldir. Ya da kaliteli kod ancak düzgün bir tasarım çalışmasının çıktısı olabilir.

Bu yazıda burada bırakalım. Belli ki basitliği ancak bir kaç yazıda tamamlayabileceğim. Bir sonraki yazıda daha detaylı bir şekilde basit kodun özelliklerinden bahsedelim.

Hoşçakalın…

Toplam görüntülenme sayısı: 9385

24 Bunu beğendim 🙂
Tweet
Follow me
Tweet to @kaldiroglu
09 Nisan 2014

Üçlü Şart Operatörü, “?:”, Muamması

Akin Java, Java Dersleri

C‘den etkilenen dillerde var olan üçlü operatör (ternary ya da n-ary operator) “?:” bence iki ucu keskin bıçak misali bir operator. Hem son derece kısa ifadeler yazmanıza imkan veriyor hem de anlaşılması zor ifadeler oluşturmanız, örneğin işten ayrılırken kodun içine ufak mayınlar bırakabilmeniz için size fırsat sağlıyor. İşin açıkcası Javacılar bu operatörü çok sık kullanmıyorlar. Hayatında hiç bu operatörü kullanmamış Java programcıları vardır diye eminim.

Önce kısaca bu operatörün yapısından bahsedip sonra sağlıklı kullanımına geleceğim.

“?:” operatörü üç tane operant ya da işlenen/argüman alır. Bunlardan ilki muhakkak boolean/Boolean bir değişken ya da boolean/Boolean üreten bir ifade olmalıdır, aksi taktirde derleme zamanı hatası alınır. Bir başka deyişle bu operatör, “if-else”in ya da “or” mantıksal işleminin kısaltılmış halidir. İlk ifade doğru ise ikinci ifade, değil ise üçüncü ifade, atamanın solundaki değere atanır.

...
int ilki = 8;

int ikincisi = 11;

int min = (ilki &lt;= ikincisi) ? ilki : ikincisi;

System.out.println(min);
...

Yukarıdaki ifadede int olan min değişkenine “ilki <= ikincisi” ifadesinin değerine göre ya ilki ya da ikincisi ile ilk değer ataması yapılacaktır. Dolayısıyla min değişkenine “8” atanacaktır.

Daha açık  bir örnek şu olabilir:

...
System.out.println(true ? "true" : "false" );
System.out.println(false ? "true" : "false" );
...

Burada dikkat edilmesi gereken şeylerden birisi de ikinci ve üçüncü ifadelerin void dondüren bir metot olmaması gerektiğidir.

...
String string1 = "I love Java very much.";
String string2 = (string1.matches(".*Java.*")) ?
                  string1 : "I love nothing.";
System.out.println(string2);
...

Bu örnekler üçlü şart operatörünün güzel kullanımlarındandır. Bu şekilde böyle daha uzun kod yazmaktan kurtulmuş oluyoruz:

...
String string1 = "I love Java.";
if(string1.matches(".*Java.*"))
   string2 = string1;
else
   string2 = "I love nothing.";
System.out.println(string2);
...

Bu operatörün iki ucu keskin olduğunu söylemiştim. Bakın şuna:

...
student.setId(student.getTcCitizen() ?
 (StrUtils.checkNullOrEmpty(studentService.getCurrentStudent().getTckn()) ?
  model.getTckn():studentService.getCurrentStudent().getTckn())
  :(StrUtils.checkNullOrEmpty(studentService.getCurrentStudent.getId()))  ?
  model.getId(): studentService.getCurrentStudent().getId());
...

Buyrun cenaze namazına di mi?

Bu kodda “?:” operatörü son derece yanlış bir şekilde kullanılmıştır. Yukarıdaki kod parçasının daha açık hallerinden birisi şöyle olabilir:

...
 // Burada Student’ın Idsi atanır. Id olarak eğer öğrenci TC
 // vatandasi ise ya servisten ya da modelden gelen tckn atanır.
 // Eğer öğrenci TC vatandasi değilse Id olarak student id
 // alınır. Burada da yine ya servisten ya da modelden gelen
 // student id atanır.

 String no = null;
 boolean isTcCitizen = student.getTcCitizen();
 String tcknFromModel = model.getTckn();
 String tcknFromService = studentService.getCurrentStudent().getTckn();
 boolean nullOrEmptyTcknFromService = StrUtils.isNullOrEmpty(tcknFromService);
 String studentIdFromService = studentService.getCurrentStudent().getId();
 boolean nullOrEmptyIdFromService = formUtils.isNullOrEmpty(studentIdFromService);

if(isTcCitizen){
   if(nullOrEmptyTcknFromService)
      no = tcknFromModel;
   else
      no = tcknFromService;

else{
   if(nullOrEmptyIdFromService)
      no = tcknFromModel;
   else
      no = studentIdFromService
}
student.setId(no);
...

İşin açıkçası yukarıdaki kodda işlenen tek günah “?:” operatörü ile ilgili değil. Bir arada bir sürü isimlendirme ve etkin kodlama hatası var. Ama en büyüğü sanırım bu operatörün kullanımında.

Allah tüm Javacıları böyle kodlar devralmak ve yazmaktan korusun 🙂

Toplam görüntülenme sayısı: 2628

10 Bunu beğendim 🙂
Tweet
Follow me
Tweet to @kaldiroglu
02 Nisan 2014

Temiz ve Kaliteli Kod Nasıl Yazılır? ya da Temiz ve Kaliteli Kodun Temel İlkeleri

Akin Java, Yazılım Mühendisliği

Bugünlerde temiz ve kaliteli kod yazma üzerine düşünmekteyim. (Aslında temiz ve kaliteli kelimelerini bundan sonra birbiri yerine geçecek şekilde kullanacağımı şimdiden belirteyim.) Bu konuda her zaman düşünüp, bildiklerimi ve çıkarımlarımı paylaşıyorum ama bu sefer daha formal bir çalışma yapıp, düşündüklerimi bir çerçeveye oturma ihtiyacı doğdu. Bu çalışmayı her ne kadar Java tabanında yapacak olsam da hemen her türlü programlama diliyle uğraşanları ilgilendirecek şeylerden bahsedeceğim. Örneğin bu çalışma C gibi prosedürel dillerle uğraşanlara faydalı olacağı gibi, C++, C# vb. nesne-merkezli dillerle uğraşan kişiler kesinlikle çok daha fazla fayda edineceklerdir. Açıkçası bu çalışmanın Java ile alakası, çoğu zaman sadece örnekler seviyesinde olacaktır.

“Temiz ve Kaliteli Kod Nasıl Yazılır” ile ilgili süreçte ilk olarak bir önceki yazıda “JavaTürk Java Kod İsimlendirme ve Şekil (Format) Standardı“nı yayınladım.

Temiz ve kaliteli kod yazmak, isimlendirme ve şekil standartlarına uymakla başlar belki ama bundan çok daha fazlasıdır. İsimlendirme ve şekil standartları, kaliteli kod için “şekil” yönünden gereklilik olabilir. Ama içerik olarak kaliteli kod yazmaya odaklanmak, şekil standartlarından daha önemlidir. Zira, içerik açısından kalite aslen pek çok defa dilden bağımsız, en az bir dil ailesine, çoğu zaman da her türlü programlama faaliyetine uygulanabilecek evrensel prensiplerden oluşur.

Ben hem şekil hem de içerik açısından kendimce bir çerçeve (framework) kurguladım. Bu çerçeveyi Java özelinde kurgulamama rağmen hemen her prensibi nesne-merkezli dillere, örneğin C++, C#, hatta pek nesne-merkezli olmayan dillere bile uygulanabilir. Bu çerceve aşağıdaki çizimde kısaca ifade edilmiştir:

Kaliteli Kod Nasıl Yazılır

Yukarıdaki çizimden de anlaşılacaği gibi oluşturduğum çerçeveye göre kaliteli kodun dört sıfatı ya da özelliği vardır:

  • Basit. Kaliteli kod her şeyden önce basittir, olabileceklerin en basitidir. Dolayısıyla daima “en basiti bu mudur?” diye sor kendine.
  • Odaklı. Her yazılan kod parçasının sadece ve sadece tek bir amacı olsun. Birden fazla amacı hiç bir yerde bir araya getirme.
  • Tam. Ne eksik ne fazla, sadece ihtiyaç için kod yaz, başka hiç bir şey yazma. İhtiyaçla ilgili ne varsa yaz, ilgisiz olan hiç bir şeyi yazma.
  • Güvenilir. Sadece normal durumları değil, anormal durumları da düşün. Hatalı halleri görmezden gelme.

Bu yazıda bu dört özelliği bir başka deyişle dört ilkeyi hızlıca özetleyip sonraki yazılarda her birini tek tek ele alacağım:

Basit kod en temelde anlaşılır koddur. Basit kod, hem şekil hem de içerik açısından rahat anlaşılır, gözü ve zihni yormaz. Bu anlamda basit olmanın şekil şartı isimlendirme ve şekil standartlarına uymaktır. Öte taraftan basit kod elde etmek için içerik olarak da basit kod yazmak gereklidir. Bu da hem odaklı hem de kısa kod yazmakla gerçekleşir.

Odaklı kod, hem basit kodun bileşenidir hem de kendi başına ozelliklere sahip olduğundan apayrı bri şekilde ele alınması gereken bir ilkedir. Odaklı olmanın iki yüzü vardır: Bir yerde sadece bir şeyi halletmek, bir şeyi sadece bir yerde halletmek.

Tam kod, ne eksik ne fazla, sadece istenilenin yapıldığı, istenilen, beklenen, ihtiyaç duyulan dışında hiç bir fazla ya da eksik bir şeyin yapılmadığı koddur.

Güvenilir kod ise sadece olması gerekeni yapmaz. Güvenilir kod olması gerekn yanında olmaması gerekenin olmayacağından emin olur, olabilecek olanları da göz önüne alır. Bu anlamda güvenilir kod, bir ihtiyacı her yönüyle halleder.

Güzel kod ya da şiir gibi kod yazabilecek kadar iyi programcı olabilmek dileğiyle…

Toplam görüntülenme sayısı: 3834

18 Bunu beğendim 🙂
Tweet
Follow me
Tweet to @kaldiroglu
«< 43 44 45 46 47 >»

Günlüğüme Hoşgeldiniz

Bu günlükte, Yazılım Mühendisliği, Bilgi Teknolojileri, Java, kişisel gelişim ve zaman zaman da diğer konulardaki düşüncelerimi sizlerle paylaşacağım. Umarım beğenir ve hoşça vakit geçirirsiniz.

Her türlü düşüncenizi, yorum olsun, beğeni ya da eleştiri olsun, bana iletmenizi rica ediyorum sizden. Ayrıca bana akin@javaturk.org adresinden ya da Twitter'dan ulaşabilirsiniz. Videolarıma da buradan ulaşabilirsiniz.

Teşekkür ederim.

Akın Kaldıroğlu

Rahat Okumak İçin

A Decrease font size. A Reset font size. A Increase font size.

Sosyal Medya

  • Twitter
  • Facebook
  • LinkedIn
  • Youtube

Son Twitlerim

→ Takip Etmek İçin

Abone Olun

Emalinizi girerek yazılardan haberdar olun.
Loading

Son Yazılarım

  • Udemy Eğitimlerim Üzerine
  • (başlıksız)
  • Clean Code / Temiz Kod Eğitimi Udemy’de
  • Java ile Nesne-Merkezli Programlamaya Giriş Eğitimi Udemy’de
  • Selsoft Video Eğitimleri
  • Spring ile Kurumsal Yazılım Geliştirme
  • Corona Günlerinde Design Patterns
  • Corona Günlerinde Java
  • JDK 10 ve “var” Özelliği
  • Onur Özcan
  • Analist ve İş Bilgisi
  • Farklı Dillerin Bakış Açısıyla Nesne-Merkezli Programlama
  • Java Nedir?
  • Bilgi Teknolojilerinde Yetenek Yönetimi – II: Tanımlar ve Eleştiriler – I
  • Alelade Hikayeler – II: Bir Başka Performans Problemi

Yazı Kategorileri

Yazı Takvimi

Mart 2026
P S Ç P C C P
 1
2345678
9101112131415
16171819202122
23242526272829
3031  
« May    

Yazı Arşivi

Blogroll

  • Binnur Kurt'un Günlüğü
  • Ender'in Java Blogu
  • Erdem Seherler
  • Kızımın Günlüğü
  • Kurumsal Java
  • Levent Karagöl
  • Levent'in Java Blogu
  • Mert Can Akkan’s java tips,options, news…
  • Yaşar Safkan
  • Yasin Saygılı
  • Yazı Dünyası

Yazı Etiketleri

analiz Bilmek C Desen design pattern EJB Eğitim Fortran Hibernate Java Java'ya nasil baslarim Java dersleri Java EE Java Persistence API Java SE Java Sertifika Java Öğren Java öğreniyorum Java öğrenmek JPA Kalıp Kurumsal Java nesne nesne-merkezli No Silver Bullet object object-oriented Oracle Java Certifications pattern performans programlama programlama dilleri programlama nedir sertifika singleton tasarım tasarım deseni tasarım desenleri tasarım şablonu yazılım yazılım geliştirme Yazılım Mühendisliği yazılımın doğası yazılımın zorlukları Şablon

↑

© Java Günlüğüm 2026
Powered by WordPress • Themify WordPress Themes
 

Yorumlar Yükleniyor...