February 16, 2011

Şu sıralarda HTML elemanlarıyla ilgileniyoruz. Hatalı durumlarda ne yapılması gerektiğinden de konuştuk. Örneğin gibi başlık elemanlarını oluşturan işleve geçersiz bir düzey değeri gelse ne yapmalı?

Şöyle bir işlev:

char[] htmlBaşlığı(int düzey, const char[] metin)
{
   string etiket = 'h' ~ to!string(düzey);
   return '<' ~ etiket ~ '>' ~ metin ~ "</" ~ etiket ~ '>';
}

unittest
{
   assert(htmlBaşlığı(3, "merhaba") == "<h3>merhaba</h3>");
}

Yasal başlık düzeyleri 1'den 6'ya kadardır. Bunların dışında bir değer geldiğinde hata atarak sayfayı oluşturmaktan vazgeçmek yerine, bir hata mesajı yazıp uygun bir değer kullanabiliriz. Örneğin 1'den küçük değerler yerine 1, 6'dan büyükler yerine de 6...

Onu sağlayan bir işlev denedim. İçinde bazı uç olanaklar da var: :)

void koşulaUydur(alias koşul)(const char[] tanım,
                             ref int değer,
                             int düzgünDeğer)
{
   if (!koşul()) {
       stderr.writefln("%s olan %s değeri %s olarak değiştiriliyor",
                       değer, tanım, düzgünDeğer);
       değer = düzgünDeğer;
   }
}

unittest
{
   int değer = -42;
   koşulaUydur!({ return değer >= 10; })("deneme", değer, 10);
   assert(değer == 10);
}

O işlev, kendisine verilen koşulu işletiyor; doğru çıkmazsa değer'i düzgünDeğer'e eşitliyor. Test kodunda görüldüğü gibi, başlangıçta -42 olan değer, istenen koşula uydurulmuş; yani 10 olmuş.

Bu işleve verilen düzgünDeğer'in koşulu sağlayacağı düşünülüyor. İşte bunu bir out bloğunda denetleyebiliriz:

void koşulaUydur(alias koşul)(const char[] tanım,
                             ref int değer,
                             int düzgünDeğer)
out
{
   assert(koşul());
}
body
{
   // ... burası aynı ...
}

Tek yaptığımız, işlevden çıkıldığında koşulun artık sağlandığını garanti etmek. Yani işlev işini görüyor ve olasılıkla bozuk olan değerleri sonuçta koşula uyduruyor.

HTML başlığı konusuna dönersek, düzeyin 1 ve 6 arasında olduğunu şu şekilde denetleyebiliriz:

char[] htmlBaşlığı(int düzey, const char[] metin)
{
   koşulaUydur!({ return düzey >= 1; })("başlık", düzey, 1);
   koşulaUydur!({ return düzey <= 6; })("başlık", düzey, 6);

   string etiket = 'h' ~ to!string(düzey);
   return '<' ~ etiket ~ '>' ~ metin ~ "</" ~ etiket ~ '>';
}

unittest
{
   assert(htmlBaşlığı(3, "merhaba") == "<h3>merhaba</h3>");
   assert(htmlBaşlığı(0, "merhaba") == "<h1>merhaba</h1>");
   assert(htmlBaşlığı(10, "merhaba") == "<h6>merhaba</h6>");
}

1 ve 6 için iki tane koşulaUydur işlevi çağırdım ve iki tane daha test ekleyerek 0 için 1, 10 için de 6 kullanıldığını denetledim. Çıkışa da uyarı olarak şunlar yazılıyor:

'0 olan başlık değeri 1 olarak değiştiriliyor
10 olan başlık değeri 6 olarak değiştiriliyor'

Şimdi, iki farklı koşulaUydur çağrısının aslında aralığaUydur anlamına geldiğini farkederek öyle bir işlev yazıyorum:

void aralığaUydur(const char[] tanım, ref int değer, int enAz, int enÇok)
{
   koşulaUydur!({ return değer >= enAz; })(tanım, değer, enAz);
   koşulaUydur!({ return değer <= enÇok; })(tanım, değer, enÇok);
}

unittest
{
   int değer = 7;
   aralığaUydur("aralık deneme", değer, 10, 20);
   assert(değer == 10);

   değer = 50;
   aralığaUydur("aralık deneme", değer, 10, 20);
   assert(değer == 20);
}

En son olarak ona da in ve out blokları ekliyorum:

void aralığaUydur(const char[] tanım,
                 ref int değer,
                 int enAz,
                 int enÇok)
in
{
   assert(enAz <= enÇok);
}
out
{
   assert(değer >= enAz);
   assert(değer <= enÇok);
}
body
{
   // ... burası aynı ...
}

Artık htmlBaşlığı işlevi bu yeni işlevi çağırabilir. Bütün kod aşağıdaki gibi. '-unittest' seçeneği ile derlemeyi unutmayın:

import std.conv;
import std.stdio;

void koşulaUydur(alias koşul)(const char[] tanım,
                             ref int değer,
                             int düzgünDeğer)
out
{
   assert(koşul());
}
body
{
   if (!koşul()) {
       stderr.writefln("%s olan %s değeri %s olarak değiştiriliyor",
                       değer, tanım, düzgünDeğer);
       değer = düzgünDeğer;
   }
}

unittest
{
   int değer = -42;
   koşulaUydur!({ return değer >= 10; })("deneme", değer, 10);
   assert(değer == 10);
}

void aralığaUydur(const char[] tanım, ref int değer, int enAz, int enÇok)
in
{
   assert(enAz <= enÇok);
}
out
{
   assert(değer >= enAz);
   assert(değer <= enÇok);
}
body
{
   koşulaUydur!({ return değer >= enAz; })(tanım, değer, enAz);
   koşulaUydur!({ return değer <= enÇok; })(tanım, değer, enÇok);
}

unittest
{
   int değer = 7;
   aralığaUydur("aralık deneme", değer, 10, 20);
   assert(değer == 10);

   değer = 50;
   aralığaUydur("aralık deneme", değer, 10, 20);
   assert(değer == 20);
}

char[] htmlBaşlığı(int düzey, const char[] metin)
{
   aralığaUydur("başlık", düzey, 1, 6);

   string etiket = 'h' ~ to!string(düzey);
   return '<' ~ etiket ~ '>' ~ metin ~ "</" ~ etiket ~ '>';
}

unittest
{
   assert(htmlBaşlığı(3, "merhaba") == "<h3>merhaba</h3>");

   // 1'den küçükse 1 olmalı
   assert(htmlBaşlığı(0, "merhaba") == "<h1>merhaba</h1>");

   // 6'dan büyükse 6 olmalı
   assert(htmlBaşlığı(10, "merhaba") == "<h6>merhaba</h6>");
}

void main()
{}

Ali

--
[ Bu gönderi, http://ddili.org/forum'dan dönüştürülmüştür. ]