Effective Java Madde 18: Kalıtım Yerine Komposizyonu Tercih Edin

Kalıtım kodların yeniden kullanımını sağlayabilen güçlü bir araçtır, ancak her zaman yapacağınız iş için en doğru araç olmayabilir. Yanlış kullanıldığında kırılgan yazılımlara yol açar. Kalıtımı, aynı paket içerisinde, ata sınıf ve çocuk sınıfın aynı programcının kontrolünde olduğu durumlarda kullanmak güvenlidir. Bunun yanında, özellikle kalıtılmak için tasarlanmış ve belgelenmiş sınıfları kalıtmak da güvenlidir (Madde 19). Ancak, farklı paketlerdeki sıradan somut sınıfları kalıtmak tehlikelidir. Hatırlatma olarak söyleyelim, burada kalıtım dediğimiz zaman bir sınıfın başka bir sınıfı kalıtmasından bahsetmekteyiz. Bahsedilen problemler arayüz (interface) kalıtırken geçerli değildir.

Metot çağırmanın aksine, kalıtım yapmak sarmalamayı (encapsulation) ihlal eder. Başka bir deyişle, bir çocuk sınıf işlevini gerçekleştirebilmek için ata sınıfın gerçekleştirim detaylarına bağımlıdır. Ata sınıfın içeriği zaman içerisinde yeni sürümlerle birlikte değişebilir, ve bu durumda çocuk sınıfın kodu hiç değişmemiş olsa bile çalışmayabilir. Sonuç olarak, eğer ata sınıf özel olarak kalıtılmak için tasarlanmamışsa, çocuk sınıf değişikliğe ihtiyacı olmasa bile ata sınıfla birlikte gelen değişikliklere uyum sağlayacak şekilde değiştirilmelidir.

Bunu somutlaştırmak için, farzedelim ki HashSet kullanan bir programımız olsun ve uygulamaya bu HashSet yaratıldığından beri kaç kere eleman eklendiğini döndüren bir metot eklemek istediğimizi düşünelim. (HashSet’in o andaki eleman sayısı ile karıştırmayın, eleman sayısı eleman sildikçe azalır.) Bunu sağlamak için HashSet sınıfını kalıtıp, her eleman eklendiğinde bir sayacı artıralım ve bu sayaç değerini döndüren bir metot ekleyelim. HashSet sınıfında add() ve addAll() olmak üzere iki tane eleman ekleyen metot vardır, dolayısıyla bunları geçersiz kılmalıyız.

// Bozuk - Yanlış kalıtım kullanımı!
public class InstrumentedHashSet<E> extends HashSet<E> {

    // Eklenen eleman sayısı
    private int addCount = 0;

    public InstrumentedHashSet() { }

    public InstrumentedHashSet(int initCap, float loadFactor) {
        super(initCap, loadFactor);
    }

    @Override
    public boolean add(E e) {
        addCount++;
        return super.add(e);
    }

    @Override
    public boolean addAll(Collection<? extends E> c) {
        addCount += c.size();
        return super.addAll(c);
    }

    public int getAddCount() {
        return addCount;
    }
}

Yukarıdaki sınıf akla yatkın görünse de aslında bozuktur. Diyelim ki bu sınıftan bir nesne yarattık ve addAll() metodunu kullanarak 3 eleman ekledik. Burada, Java 9 ile eklenen List.of statik metodunu kullanıyoruz, daha önceki versiyonlar için Arrays.asList kullanın.

InstrumentedHashSet<String> s = new InstrumentedHashSet<>();
s.addAll(List.of("Snap", "Crackle", "Pop"));

Bu noktada getAddCount() metodunun 3 değeri döndürmesini bekleriz ama aslında 6 döndürecektir. Yanlış giden nedir? HashSet sınıfındaki addAll() metodu aslında kendi içerisinde add() metodunu çağıracak şekilde gerçekleştirilmiştir. Dolayısıyla, InstrumentedHashSet çocuk sınıfındaki addAll() metodu addCount değerine 3 ekledikten sonra ata sınıftaki aynı isimli metodu super.addAll() ile çağırmaktadır. Ata sınıf kendi gerçekleştirimi gereği addAll() içerisinde add() metodunu çağırmakta, bu metot da çocuk sınıfta geçersiz kılındığı için çocuk sınıftaki add() metodu işletilmektedir, bu yüzden de her bir eleman için 2 defa addCount sayacına ekleme yapılmaktadır.

Bu problemi addAll() metodunu geçersiz kılmayarak (override etmeyerek) çözmek mümkündür. Sonuçta elde edeceğimiz sınıf çalışacaktır ancak bu sefer de sınıfın doğru çalışması ata sınıfın addAll() içerisinde add() metodunu çağırmasına bağımlı olacaktır. Bu tamamen HashSet sınıfını yazanların tercihidir ve ileriki Java sürümlerinde öyle kalacağının hiçbir garantisi yoktur. Bu sebeple de yazdığımız çocuk sınıf o an için çalışsa da ata sınıftaki değişikliklere karşı çok kırılgan olacaktır.

Alternatif olarak, addAll() metodunu ata sınıfı hiç çağırmadan verilen veri yapısı üzerinde dolaşıp her eleman için çocuk sınıftaki add() metodunu çağıracak şekilde geçersiz kılabiliriz. Bu durumda ata sınıfın addAll() içerisinde add() metodunu çağırdığı varsayımından kurtularak sınıfın her şekilde doğru çalışmasını sağlayabilir, kırılganlık problemini çözebiliriz. Çünkü ata sınıftaki addAll() metodu devreden tamamen çıkmış olacaktır. Ancak bu yöntem de bütün problemleri çözmez. Ata sınıf içerisindeki detaylar her zaman bu kadar açık olmayacaktır ve bulması zaman alacak, kolay hata yapmaya sebebiyet verecektir. Hatta bazen bu yöntemi uygulamak mümkün olmayacaktır çünkü bazı metotlar çocuk sınıfın erişemediği, ata sınıf içerisindeki private alanlara ihtiyaç duyacaktır.

Çocuk sınıflarda kırılganlığa sebebiyet veren bir başka unsur da ata sınıfların yeni sürümlerle birlikte yeni metotlar eklemesidir. Farzedelim ki bir uygulama güvenlik gerekçeleriyle bir veri yapısına eklenen her bir elemanı ön kontrolden geçiriyor olsun. Bunu sağlamak için veri yapısını temsil eden sınıf kalıtılarak eleman ekleyen her metot geçersiz kılınıp, eleman eklemeden önce güvenlik kontrolünü yapan kod parçası çalıştırılabilir. Bu çocuk sınıf o an için çalışacaktır ancak ata sınıf bir sonraki sürümde eleman ekleyen yeni bir metot tanımlarsa çocuk sınıfta var olmayan bu metot güvenlik kontrolü yapmadan veri yapısına eleman eklemeye başlayacaktır. Bu anlatılan teorik bir problemden ibaret değildir, Hashtable ve Vector sınıfları Collections çatısına eklenirken bu şekilde birkaç güvenlik açığı oluşmuş ve sonradan fark edilerek düzeltilmiştir.

Bu iki problem de ata sınıftaki metotları geçersiz kılmaktan dolayı çıkmaktadır. Bu yüzden bir sınıfı kalıtıp metotlarını geçersiz kılmadan, sadece çocuk sınıfa eklemeler yaptığınızda daha güvenli olacağını düşünebilirsiniz. Bu şekilde yapılan bir kalıtım daha güvenli olsa da, tamamen risksiz değildir. Ata sınıf ileriki bir sürümde, sizin çocuk sınıfa eklediğiniz bir metodu aynı imza ama farklı bir dönüş türüyle tanımlarsa çocuk sınıf derleme hatası verecektir. Eğer dönüş türü de aynı olursa bu sefer çocuk sınıf ata sınıftaki metodu siz farkında olmadan geçersiz kılmış olacaktır, dolayısıyla yukarıda bahsedilen iki problem tekrar karşınıza çıkacaktır. Dahası, çocuk sınıfta var olan metodun ata sınıftaki yeni metodun sözleşmesini karşılayacağını bilemezsiniz, çünkü siz çocuk sınıfı yazarken sözleşme belirlenmemişti bile.

Neyse ki, burada anlatılan bütün problemlerden sakınmanın bir yolu var. Var olan bir sınıfı kalıtmak yerine, yeni yazdığınız sınıfın içine ihtiyacınız olan sınıfın referansını private bir alan olarak ekleyin. Bu teknik kompozisyon olarak bilinmektedir çünkü bir sınıf diğerinin bileşeni durumuna gelmektedir. Burada yeni yazdığınız sınıf, referans olarak eklediğiniz sınıfın metotlarını çağırarak oradan gelen sonuçları döndürecektir. Buna “iletme” de denir çünkü yeni sınıfta tanımlı metotlar gelen isteği referans sınıfa iletmektedir. Sonuç olarak ortaya çıkan sınıf sapasağlam ve içerdiği sınıfın gerçekleştirim detaylarından tamamen bağımsız olacaktır. Referans olarak eklenen sınıfa yeni metotlar eklense bile sizi etkilemeyecektir. Bunu somutlaştırmak için InstrumentedHashSet sınıfını komposizyon ve iletme uygulayarak tekrar yazalım. Burada dikkat edilmesi gereken nokta, sınıfın iki parçaya bölünmüş olmasıdır: sınıfın kendisi ve sadece iletim yapan yardımcı bir gömülü sınıf.

// Kalıtım yerine komposizyon kullanılan çözüm
public class InstrumentedSet<E> extends ForwardingSet<E> {
    private int addCount = 0;

    public InstrumentedSet(Set<E> s) {
        super(s);
    }

    @Override
    public boolean add(E e) {
        addCount++;
        return super.add(e);
    }

    @Override
    public boolean addAll(Collection<? extends E> c) {
        addCount += c.size();
        return super.addAll(c);
    }

    public int getAddCount() {
        return addCount;
    }
}

// Yeniden kullanılabilir iletim sınıfı
public class ForwardingSet<E> implements Set<E> {

    private final Set<E> s;
    public ForwardingSet(Set<E> s) {
        this.s = s;
    }

    public void clear() {
        s.clear();
    }
    public boolean contains(Object o) {
        return s.contains(o);
    }
    public boolean isEmpty() {
        return s.isEmpty();
    }
    public int size() {
        return s.size();
    }
    public Iterator<E> iterator() {
        return s.iterator();
    }
    public boolean add(E e) {
        return s.add(e);
    }
    public boolean remove(Object o) {
        return s.remove(o);
    }
    public boolean containsAll(Collection<?> c) {
        return s.containsAll(c);
    }
    public boolean addAll(Collection<? extends E> c) {
        return s.addAll(c);
    }
    public boolean removeAll(Collection<?> c) {
        return s.removeAll(c);
    }
    public boolean retainAll(Collection<?> c) {
        return s.retainAll(c);
    }
    public Object[] toArray() {
        return s.toArray();
    }
    public <T> T[] toArray(T[] a) {
        return s.toArray(a);
    }

    @Override
    public boolean equals(Object o) {
        return s.equals(o);
    }

    @Override
    public int hashCode() {
        return s.hashCode();
    }

    @Override
    public String toString() {
        return s.toString();
    }
}

Yukarıdaki sınıfı yazabilmemize olanak sağlayan şey HashSet tarafından da uygulanan Set arayüzünün varlığıdır. Sağlam olmasının yanında bu sınıf aynı zamanda çok da esnektir. InstrumentedSet sınıfı Set arayüzünü uygulamaktadır ve yine Set türünde parametresi olan bir yapıcı metot içermektedir. Aslında bu sınıfın yaptığı iş, bir Set nesnesini başka bir Set nesnesine dönüştürüp çeşitli eklemeler yapmaktan ibarettir. Tek bir somut sınıf ile kullanılabilen kalıtım tabanlı yaklaşımın aksine, bu çözüm daha esnektir çünkü Set arayüzünü gerçekleştiren herhangi bir sınıf ile kullanılabilir.

Set<Instant> times = new InstrumentedSet<>(new TreeSet<>(cmp));
Set<E> s = new InstrumentedSet<>(new HashSet<>(INIT_CAPACITY));

Hatta daha önceden yaratılmış ve kullanılmış olan Set türünden bir nesneyi bile dönüştürüp yeni haliyle kullanmaya devam edebilirsiniz.

static void walk(Set<Dog> dogs) {
    InstrumentedSet<Dog> iDogs = new InstrumentedSet<>(dogs);
    ... // bu metot içerisinde dogs yerine iDogs kullanabilirsiniz
}

Bu örnekteki InstrumentedSet sınıfı sarmalayan (wrapper) sınıf olarak tanımlanabilir çünkü içerisinde başka bir Set nesnesi barındırmakta, başka bir deyişle sarmalamaktadır. Bu yaklaşım aynı zamanda bu dekoratör tasarım deseni olarak da adlandırılmaktadır çünkü bizim örneğimizde InstrumentedSet bir Set nesnesini alıp onu dekore etmektedir.

İletim sınıfı içerisindeki bütün iletim metotlarını yazmak biraz zahmetli olsa da bir kere yazıldıktan sonra yeniden kullanılabilir olması avantajlıdır. Örneğin, Guava kütüphanesi bütün collections arayüzleri için iletim sınıfları tanımlamaktadır.

Kalıtım kullanmak sadece çocuk sınıf ata sınıfın bir alt türü ise mantıklıdır. Eğer B sınıfının A sınıfını kalıtması gerektiğini düşünüyorsanız, bunu yapmadan önce mutlaka “B gerçekten bir A mı?” sorusunu sorun. Eğer bu soruya gerçekten evet cevabını veremiyorsanız o zaman B sınıfının A’yı kalıtması yanlış olacaktır.
Eğer cevap hayır ise, B sınıfı A türünde private bir referans tanımlamalı ve dışarıya daha küçük ve basit bir API sunmalıdır. Bu durumda A sınıfı B’nin gerekli bir parçası değil gerçekleştirim detayı olarak kalacaktır.

Java kütüphaneleri içerisinde bu kurala aykırı olan birçok sınıf vardır.
Mesela stack (yığın) bir vektör olmamasına rağmen Stack sınıfı Vector sınıfını kalıtmaktadır. Benzer olarak Properties sınıfının Hashtable sınıfını kalıtması da yanlış bir tercihtir ve bu iki durumda da kompozisyon kullanmak daha yararlı olurdu diyebiliriz.

Eğer kompozisyonun uygun olduğu yerde kalıtım kullanıyorsanız sınıfın gerçekleştirim detaylarını gereksiz bir biçimde dışarıya açıyorsunuz demektir. Sonuçta ortaya çıkan API sınıfın gerçekleştirimine bağımlı kalacak ve ileride değişiklik yapılmasını kısıtlayacaktır. Daha da önemlisi, istemcilerin gerçekleştirim detaylarına direk olarak erişebilmesine neden olursunuz, bu da en iyi ihtimalle istemcilerin sınıfınızı yanlış kullanmasına sebep olabilir. Örneğin, p referansı Properties türünden bir nesneyi gösteriyorsa, p.getProperty(key) ile p.get(key) farklı sonuçlar döndürebilir çünkü birinci metot varsayılan değerleri de hesaba katarken Hashtable sınıfından kalıtılan ikinci metot bunu yapmamaktadır. Kötü ihtimali düşünecek olursak da istemcilerin gerçekleştirim detaylarına erişebiliyor olması sizin sınıfınızı bozmalarına bile neden olabilir. Properties sınıfını tasarlayanlar aslında sadece String türünden anahtarlar ve değerler kullanılmasını istemişlerdir ancak kalıtım kullanılması ile arka taraftaki Hashtable metotlarına da ulaşabilen istemciler bu kuralı kolaylıkla ihlal edebilirler. Bu kural bir kere ihlal edildiğinde ise Properties sınıfının diğer kısımları (load, store gibi) kullanılamaz hale gelmektedir. Bu problem fark edildiğinde düzeltmek için çok geçti çünkü istemciler String olmayan anahtarlar ve değerler kullanmaya çoktan başlamışlardı.

Kalıtım kullanmaya karar vermeden önce kendinize sormanız gereken son soru da şudur: Kalıtmak istediğim sınıfta bir API hatası var mı? Var ise benim aynı hatayı kendi sınıfıma yansıtmam kabul edilebilir mi?
Kalıtım kullandığınızda ata sınıfın API’ını da kalıtmış oluyorsunuz yani hatalar varsa onlar da beraberinde gelecektir, ancak kompozisyon kullanırsanız diğer sınıfın bütün detaylarını saklayıp sıfırdan bir API oluşturabilirsiniz.

Özetleyecek olursak, kalıtım güçlüdür ancak sarmalamayı (encapsulation) ihlal ettiği için problemlidir. Sadece çocuk sınıf ile ata sınıf arasında bir tür-alt tür ilişkisi varsa kullanılması uygun olur. Ancak o zaman bile, eğer ata sınıf ile çocuk sınıf farklı paketlerde tanımlanmışsa ve ata sınıf kalıtılmak için tasarlanmamışsa yazılımda kırılganlığa yol açar. Bu kırılganlığı önlemek için kompozisyon ve iletim sınıfı kullanmayı tercih edin. Bu şekilde tasarlanan sınıflar hem daha esnek hem de daha sağlam olacaktır.

Share

Java’da Enum Türleri

Merhabalar, Java dilinde Enum türlerini detaylarıyla anlatan Türkçe bir kaynak bulamadığım için bu yazıyı yazmaya karar verdim, umarım bu alandaki eksiği bir nebze olsun kapatır.

Java’da Enum türleri önceden tanımlanmış sabit değerleri ifade etmek için kullanılır. Peki bununla neyi kastediyoruz? Mesela en klasik örnek haftanın günleri. Bir haftanın kaç gün olduğu ve hangi günlerden oluştuğu bilindiğine göre bunu aşağıdaki gibi bir Enum türüyle ifade edebiliriz.

public enum Gun {
    PAZARTESI,
    SALI,
    CARSAMBA,
    PERSEMBE,
    CUMA,
    CUMARTESI,
    PAZAR
}

Read more “Java’da Enum Türleri”

Share

Birim Test Nedir? Niçin Yapılır? Nasıl Yapılır?

Yıl olmuş 2014 hala birim test makalesi mi yazıyorsun diye düşünüyor olabilirsiniz. Ancak, birim testi uzun zamandır kullanılan bir yöntem olmasına rağmen tam olarak ne işe yaradığı, neden yapıldığı ve nasıl yapılması gerektiği konusunda açıklayıcı bir Türkçe yazı bulmak malesef zor. Bu yüzden, yazılım mühendisi adaylarına ve kariyerine yeni başlayan arkadaşlara faydalı olabilmek adına bu yazıyı yazmak uygun olur diye düşündüm.

Birim Testi Nedir?

Birim testi adından anlaşıldığı üzere yazılım birimlerinin test edilmesidir. Burada yazılım birimi dediğimiz şey ise test edilebilen en küçük yazılım bileşenidir. Nesneye yönelik programlama yaklaşımını ele alacak olursak, yazılım birimleri sınıflardır diyebiliriz. Yapılan şey basit olarak sınıf davranışlarının (metodlar) belirli girdiler sağlandığı zaman doğru bir şekilde çalışıp, istediğimiz sonucu üretip üretmediğini kontrol etmektir. Bu şekilde yazılımın küçük birimleri test edildiği zaman, bütünü oluşturan parçaların en azından kendi içlerinde çalıştığından emin olmuş oluruz. Buraya kadar söylediklerimiz birim testinin genel tanımıdır, ama yazılım geliştiren kişiler olarak asıl anlamamız gereken şey birim testini niçin yaptığımızdır.

Birim Test Niçin Yapılır?

Bu soruyu eminim ki birçok yazılımcı kendi kendine sormuştur. Bir kısmımız tam olarak neye hizmet ettiğini anlamasak da, faydalı olduğunu düşündüğümüz için ve kendimizi daha güvende hissetmek adına birim test yazarız. Bazılarımız ise birim test yazmanın faydalı olduğunu bilmemize rağmen çeşitli bahaneler üreterek birim test yazmaktan kaçarız. Bunun arkasındaki asıl sebep ise birim testlerin ve test odaklı yazılım geliştirme tekniğinin (test-driven development) asıl amacını kavrayamamış olmamızdır. Her şeyden önce şunu söylemek gerekir: Birim testleri yazılımları test etmek için yazılmaz. İsmi “birim test” olan bir yöntem için “asıl amacı yazılımları test etmek değildir” demek ilk başta çok mantıklı gelmeyebilir ama yazıyı okudukça bana hak vereceğinizi düşünüyorum.

Birim testler hata bulmak için değildir

Bir yazılım sistemindeki hataları (bug) bulmak birim testler ile mümkün değildir. Çünkü birim testlerin yaptığı iş yazılımın en küçük parçalarını kendi içerisinde test etmektir. Peki bu küçük parçaların kendi içlerinde çalışıyor olması, yazılımın gerçek kullanıcılar tarafından kullanılmaya başladığı zaman bir bütün olarak çalışacağını gösterir mi? Kesinlikle hayır. Bir yazılım sistemi, onu oluşturan parçaların toplamından çok daha fazlasıdır. Dolayısıyla bu bütünü test etmek için farklı yöntemler kullanmak gerekir. İşlevsel test (functional testing), bütünleştirme testi (integration testing) bunlara örnek verilebilir ancak konumuz birim test olduğu için bunlara değinmeyeceğim.

Hataları bulamıyorsa birim testler ne işe yarıyor?

Birim test yazmanın sağladığı gerçek fayda, bizi kaliteli kod yazmaya teşvik etmesidir. Peki bu nasıl olur? Öncelikle şunu söylemek gerekir ki, birim test yazmanın birinci kuralı test etmekte olduğumuz sınıfı, bağımlı olduğu diğer bütün bileşenlerden izole etmektir. Örnek verecek olursak, test ettiğiniz sınıfın bir Google servisine bağlanarak veri çektiğini düşünün. Ancak birim test esnasında bu sınıfın Google servisine bağlanıp veri çekmesini istemeyiz. Çünkü birim testin amacı yazılımın Google servisleriyle çalışabildiğini kanıtlamak değildir. Birim test yazarken, bağlantılı olduğumuz diğer bütün parçaların sorunsuz biçimde çalıştığını varsayarak yazarız, çünkü odaklandığımız şey sınıfın kendisidir, bağımlı olduğu diğer bileşenler değil. Bu varsayımı yapabilmek için de, mocking dediğimiz tekniği kullanarak test esnasında gerçek Google servisine bağlanmak yerine bizim yarattığımız sahte bir servise (mock object) bağlanıp sınıfın ihtiyacı olan veriyi döndürürüz.  Bu şekilde test ettiğimiz sınıf dışarıda bir servise bağlanmadan ihtiyacı olan veriyi alır ve işletimini tamamlar.

Şimdi test etmekte olduğumuz bu sınıfın dışarıdaki Google servisiyle sıkı sıkıya bağlı (tightly coupled) olduğunu düşünün. Sınıf Google servisiyle ilgili bütün bilgileri içinde barındırıyor ve bağlantıyı yaratıp kullanıyor, veri alışverişini yapıyor. Biz bu sınıfa gerçek Google servisine değil de bizim belirlediğimiz sahte servise (mock object) bağlanmasını nasıl söyleyeceğiz? Bu şekilde birbirine sıkıca bağlanmış yazılım bileşenlerini birbirlerinden bağımsız bir şekilde test etmek mümkün değildir. Ancak bu bileşenler gevşek bağlı (loosely coupled) olsaydı, biz sınıfımıza test esnasında sahte servisi, gerçek işletim esnasında ise Google servisini kullanmasını söyleyebilirdik. Bu şekilde yazılım bileşenlerini birbirlerine gevşek bir biçimde bağlamak Dependency Injection tekniğiyle mümkündür ve gevşek bağlı sistemler çok daha kolay bakım yapılabilen, test edilebilen ve eklemeler yapması çok daha kolay olan sistemlerdir.

Test odaklı yazılım geliştirme yapıyorsak (test-driven development), birim testleri sınıfın kendisinden önce yazmamız gerektiği için bu tarz tasarım detaylarını henüz işin başındayken doğru bir şekilde belirlemiş oluruz. Doğru biçimde birim test yazmak, yazılım bileşenlerini birbirlerine sıkı sıkıya bağlamamızı engelleyerek daha tasarım aşamasındayken daha kaliteli bir yazılım çıkarmamıza yardımcı olur. Özet olarak şunu söylemekte fayda var, bütün bileşenleri birbirinden bağımsız olarak test edilebilen yazılımlar, bakımı nispeten daha kolay olan ve kaliteli yazılımlardır. İşe birim testleri yazarak başlamak da bunu başarmamıza yardımcı olur.

Birim test yazmak kodda iyileştirme yapmayı (refactoring) kolaylaştırır

Birim test yazmanın bir diğer büyük faydası da kodda iyileştirme yaparken (refactoring) ortaya çıkar. Hiçbir kod mükemmel değildir ve iyileştirme her zaman bir ihtiyaçtır. Ancak birçok yazılımcı çalışan sistemi bozmaktan korktuğu için iyileştirme yapmaz. Ancak kapsamlı birim testleriniz varsa, değişiklik yaptığınız sınıfın hala çalışıp çalışmadığını anlamak için birim testlerinizi kullanabilirsiniz. Daha önce birim testlerin hataları bulmak için kullanılmadığını söylemiş olsak da iyileştirme esnasında üzerinde çalıştığımız sınıfı bozup bozmadığımızı anlamak mümkün olabilir. Dolayısıyla birim test yazmak sadece kodu yazarken kaliteli yazmaya teşvik etmekle kalmaz, aynı zamanda ileride kodu iyileştirmemize de yardımcı olur.

Doğru birim test nasıl yazılır?

Birim testin nasıl yazılması gerektiği de çok önemlidir. Doğru yazılmayan birim testler bize hiçbir şey kazandırmayacağı gibi en ufak değişiklikte hatalar vermeye başlayıp başımızı ağrıtırlar. Üstüne bir de testlere bakım yapmakla uğraşmak zorunda kalacağımız için de fayda sağlamanın aksine zararlı olabilirler. O yüzden birim test yazarken aşağıdaki noktalara dikkat etmekte fayda var:

  • Tek bir şeye odaklanınHer testin tek bir şeyi test ettiğinden emin olun. Çok gerekli değilse aynı test içerisine birden fazla assert ifadesi koymayın.
  • Bağımlılıkları (dependency) değil, tek bir sınıfı test edin: Yazıda daha önce de değindiğimiz gibi, bir sınıfı test ederken o sınıfı bağımlı olduğu diğer yazılım bileşenlerinden izole edin, aksi taktirde yazdığınız test birim test değildir.
  • Yazdığınız testler birbirini etkilemesin: Yazdığınız her test birbirinden bağımsız bir şekilde tek başına sorunsuz çalışabilmelidir. Eğer yazdığınız bir birim test başka bir birim testin üreteceği veriye bağımlıysa yanlış yapıyorsunuz demektir.
  • Testlerinizi doğru isimlendirin: Test sayısı arttıkça isimlendirmenin önemi de artar. Kafa karıştırıcı test isimleri kullanmak ileride problemlere yol açar. Açıklayıcı olması için test isimlerini uzun tutmanız gerekiyorsa öyle yapın, uzun isimler yanlış isimlerden daha faydalıdır.
  • Test koduna ikinci sınıf kod muamelesi yapmayın: Testler de yazılımın bir parçasıdır. Dolayısıyla normal program kodunu yazarken ne kadar özen gösteriyorsanız test kodlarına da aynı özeni gösterin, kod tekrarlarından kaçının, okunabilir test kodu yazın.
Share

Redgate SQL Search Plugin

SQL Server Management Studio 2008 kullanarak veritabanınızdaki bütün tablolar veya saklı yordamlar (stored procedure) içerisinde bir metin aramanız gerektiği zaman yapamadığınızı anlamak uzun sürmüyor. Benim de bugün bütün stored procedureları taramam gerektiğinde üçüncü parti yazılımlara yöneldim Redgate firmasının geliştirdiği SQL Search Pluginini buldum. Plugin SQL Server Management Studio içerisine entegre oluyor ve veritabanınızdaki bütün objeleri arayıp metin bulmayı çok kolaylaştırıyor. Oldukça başarılı olan bu eklenti aynı zamanda bedava. Mutlaka deneyin..

http://www.red-gate.com/products/sql-development/sql-search/

Share

Yazılım Testi Nedir?

Yazılım testi, yazılımın daha önce tanımlanmış teknik ve işlevsel gereksinimleri karşılayıp karşılamadığının ve yazılımın beklendiği gibi çalışıp çalışmadığının kontrolüdür. Yazılım test süreci de temel olarak elde edilen ürünün beklenen kalitede olduğunu belirlemek, değilse istenilen kaliteye ulaştırılmasını sağlamayı amaçlayan bir süreçtir. Kalite düzeyi müşteri tarafından maliyet analizi göz önüne alınarak belirlenir. Bilgisayar sistemleri hatalar barındırır. Bu hatalar insan (geliştirici, analist vb.) ve donanım kaynaklı olabilir ve hatalar maddi ve/veya prestij kayıplarına neden olabilir. Yazılım test surecinin proje döngüsüne katılmasıyla bu kayıplar en aza indirebilir. Burada unutulmaması gereken şey mükemmel yazılım olmadığı ve bir yazılımın asla %100 test edilemeyeceğidir. Bir yazılımı %100 oranında test etmeye çalışmak hem maddi gerekçelerle hem de pratik olarak mümkün olmadığından, yazılımın beklendiği gibi çalıştığını gösterebilecek sınırlı sayıda ancak özenle seçilmiş bir test kümesinin belirlenmesi ve bu test kümesi üzerinden test yapılması daha mantıklıdır.

Read more “Yazılım Testi Nedir?”

Share

IBM Yazılım Akademisi 2011 Duyuruldu

IBM’in 2008 yılında başlattığı ve üniversite öğrencilerinin takımlar halinde yarışarak IBM’in önerdiği proje tanımlarını hayata geçirmek için çaba sarfettikleri bir proje yarışması olan Yazılım Akademisi bu sene “Hizmet Olarak Yazılım” temasıyla yapılacak. Toplamda 26 adet proje tanımının olduğu bu seneki yarışma büyük çekişmelere sahne olacak gibi görünüyor. Sene içerisinde çeşitli eğitimlerin de verileceği yarışmada proje başlıkları şöyle:

Read more “IBM Yazılım Akademisi 2011 Duyuruldu”

Share