Sorumluluk Sahibi Olmak

Yazılım yapmayı zorlaştıran her zaman kod birimleri arasındaki bağimlılıklar ve bu bağımlılıkların yönetimi olmuştur. Bu bağımlılıkları tamamen yok etmek yazılım sistemini anlamsız kılarken, kontrolden çıkmalarına göz yummak yazılım sisteminin ölüm fermanı olabilir. Yazılım mühendisi bunu bilir ve gerekli gördüğü yerlerde DIP, ISP ve SRP gibi tasarım prensiplerini kullanarak kodu dokur.

Yazılımcının kod yazarken devamlı uygulaması gerektiği bir tasarım prensibi varsa, bu da SRP (Single Responsibility Principle) tek sorumluluk prensibidir. Bu prensibe göre her kod biriminin sadece ve sadece bir sorumluluk alanı, yani yaptığı tek bir iş olmalıdır. Bu kod birimi bir paket (package), bir sınıf, metot ya da bir değişken olabilir. SRP uygulanmadığı taktirde yazılım sistemi kontrol edilemez, kırılgan bir bağımlılıklar yumağı haline gelebilir.

Genelde bir bakışta bir kod biriminin SRP uyumlu olup, olmadığını anlamak zor değildir. Eğer bir sınıf iki bin satırdan oluşuyorsa, bu sınıfın birden fazla işle meşgul olduğu söylenebilir, aksi taktirde bu kadar büyümesi mümkün olmazdı.

Büyük yazılım sistemlerini sınıf sınıf gezip, kim SRP uyumlu, kim değil diye kontrol etmek mümkün değil. Yazılımcı olarak daha ziyade anlık resmi görmemizi sağlayacak bir araç oluşturmamız lazım. Bu aracı geliştirmeden önce, sınıfların SRP uyumluluklarını ölçmek için bir yönteme ihtiyacımız var. Bu yöntem örneğin bir sınıfın versiyon kontrol sistemi bünyesinde ne kadar değişikliğe uğradığını ölçebilir. Eğer bir sınıf sıkça değişikliğe uğruyorsa, bu bu sınıfın birden fazla sorumluluk sahibi olduğu anlamına gelebilir. Gerçekten de binlerce satırdan oluşan kod birimlerinin versiyon kontrol sistemindeki geçmişleri incelendiğinde, çok sık değişikliğe ugradıklarını görmek mümkündür. Değişik sorumluk sahibi bir sınıf, her bir sorumluluk için değişikliğe maruz kalabileceğinden, bu gibi sınıfların sicilleri kabarıktır. O zaman bu sınıfları, kaç satır ihtiva ettiklerini ve kaç sefer değişikliğe uğradıklarını tespit edebilecek bir uygulama geliştirelim. Böyle bir uygulamanın kodu aşağıda yer almaktadır.

import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.Map;

public final class SvnHistory {

	private static final String PROJECT_ROOT = "C:/xxx";
	private static Map<String, ClassStats> STATS_MAP = new HashMap<String, ClassStats>();
	private static StringBuilder STATS = new StringBuilder();
	private static final String SVN_EXE = "C:/Program Files/SlikSvn/bin/svn.exe";
	private static final String SVN_LOG_COMMAND = "log";

	public static void main(final String[] args) {
		makeHistory(new File(PROJECT_ROOT));
		makeHeader();
		makeLines();
		printStats();
	}

	private static void makeHeader() {
		STATS.append("Filename;revisionCount;loc").append("\n");
	}

	private static void makeLines() {
		for (final String key : STATS_MAP.keySet()) {
			makeLine(key, STATS_MAP.get(key));
		}
	}

	private static void printStats() {
		System.out.println(STATS.toString());
	}

	private static void makeLine(final String key, final ClassStats s) {
		STATS.append(key).append(";").append(s.revCount).append(";")
				.append(s.loc).append("\n");
	}

	private static void makeHistory(final File root) {
		for (final File file : root.listFiles()) {
			if (isJavaFile(file)) {
				history(file);
			}
			if (file.isDirectory()) {
				makeHistory(file);
			}
		}
	}

	private static boolean isJavaFile(final File file) {
		return file.getName().endsWith(".java");
	}

	private static void history(final File file) {
		try {
			final int revCounter = getRevisionCount(file);
			STATS_MAP.put(file.getName(), ClassStats.make(file.getName(), revCounter,
					getLinesOfCode(file)));
		} catch (final Exception err) {
			err.printStackTrace();
		}
	}

	private static int getRevisionCount(final File file) throws IOException,
			InterruptedException {
		String line = null;
		int revCounter = 0;
		final Process p = Runtime.getRuntime().exec(
				SVN_EXE + " " + SVN_LOG_COMMAND + " " + file.getAbsolutePath());
		final BufferedReader bri = new BufferedReader(new InputStreamReader(
				p.getInputStream()));
		final BufferedReader bre = new BufferedReader(new InputStreamReader(
				p.getErrorStream()));
		while ((line = bri.readLine()) != null) {
			if (line.startsWith("r"))
				revCounter++;
		}
		bri.close();
		bre.close();
		p.waitFor();
		return revCounter;
	}

	private static int getLinesOfCode(final File file) {
		int loc = 0;
		try {
			final FileInputStream fstream = new FileInputStream(file);
			final DataInputStream in = new DataInputStream(fstream);
			final BufferedReader br = new BufferedReader(new InputStreamReader(in));
			while (br.readLine() != null) {
				loc++;
			}
			in.close();
		} catch (final Exception e) {
			System.err.println("Error: " + e.getMessage());
		}
		return loc;
	}

	private static class ClassStats {
		private String fileName;
		private int revCount;
		private int loc;

		private ClassStats() {

		}

		public static ClassStats make(final String name, final int revCounter, final int loc) {
			final ClassStats classStats = new ClassStats();
			classStats.fileName = name;
			classStats.revCount = revCounter;
			classStats.loc = loc;
			return classStats;
		}
	}
}

Bu uygulama kodun Subversion tabanlı bir versiyon kontrol sisteminde tutulduğunu varsaymaktadır. PROJECT_ROOT değişkeni versiyon kontrol sisteminden edinilmiş projenin (working copy) ana dizinine işaret ediyor. Uygulama ana dizinde bulunan tüm dizinleri taradıktan sonra bulduğu her Java dosyası için versiyon kontrol sicilini kontrol ediyor ve dosya üzerinde yapılan değişiklik adedini tespit ediyor. Akabinde her Java dosyanın satır adedi tespit edildikten sonra, aşağıdaki şekilde bir ekran çıktısı oluşturuluyor:

Filename;revisionCount;loc
AAA.java;13;55
BBB.java;21;325
CCCjava;3;292
DDD.java;2;367
EEE.java;6;384
FFF.java;1;156
GGG.java;1;77
HHH.java;11;47
III.java;10;81
JJJ.java;3;132
KKK.java;1;111
LLL.java;9;29
MMM.java;32;433
NNN.java;45;1301
OOO.java;3;88
PPP.java;11;47
QQQ.java;60;212

Ekran çıktısı bir csv (comma seperated value) dosyası. Bu dosyayı örneğin Excel ile import edip, aşağıdaki şekilde bir diagram oluşturmak mümkün.

Diagram üzerindeki her nokta bir Java dosyasına işaret etmektedir. X ekseninde her dosyanın maruz kaldığı değişiklik adedi (revision count), Y ekseninde dosyanın sahip olduğu satır adedi yer almaktadır. Bu diagrama bakıldığında tek sorumluluk prensibine ters düşen kod birimleri hangileridir?

SRP uyumlu olmayan sınıflar için sağ üst alana bakmak yeterli. Bu sınıfları lokalize ettikten sonra tek bir iş yapacak hale getirmek biz yazılımcıların asli görevlerinden birisi. İzcilerden kendimize örnek alalım :)

EOF(End Of Fun)
Özcan Acar

Share Button
0.00 avg. rating (0% score) - 0 votes

2 Comments

  • waymnsniweere

    01 Mayıs 2013

    [url=http://atlantis-sig.ru.com/avto-page-id3459.html]автотехническая экспертиза электрогорск[/url]
    эксперт оценщик левобережный
    [url=http://atlantis-sig.ru.com/avto-page-id92.html]автотехническая экспертиза электрозаводская[/url]
    эксперт оценщик ленинградское шоссе
    [url=http://atlantis-sig.ru.com/avto-page-id6210.html]автотехническая экспертиза электросталь[/url]
    эксперт оценщик ленинский проспект

  • Wasekedge

    09 Mayıs 2013

    [url=http://gruzoperevozki-po-vsei-rossii.ru/]Грузоперевозки по России[/url] имеют много преимуществ, которые позволяют заказчикам остановить особенный избрание именно на данном виде перевозок. Компания осуществляет регулярные высококачественные перевозки грузов любого назначения в любом направлении в пределах территории Российской Федерации.

Bir cevap yazın