Amacı hatırlamaya çalışıyorum: veri tabanı gibi çalışan bir sınıf oluşturmak istiyoruz. Bu sınıfın nesnelerinin içine attığımız veriler bir dosyada belirecekler.
Bir soru: Daha önceden dosyada bulunan veriler de geçerli olacaklar mı? Yani "ali" verisine yazdığımızda var olan veri mi değişecek, yoksa dosyanın sonuna yeni bir "ali" mi eklenecek?
Dosya içindeki belirli bir kayıda gitmek çok zordur. Özellikle metin halindeki dosyalarda kayıtların uzunlukları da eşit olmayacaklarından, "'ali' kaydını bul" gibi bir işlem için bütün dosyanın baştan taranması gerekir. (Hızlandırıcı yöntemler de kullanılabilir ama bir sürü karmaşıklık getirir.)
Konuya bir kere de elimizdeki araçlarla başlayarak bakalım. Zaten eşleme tabloları var. O neden yetmiyor? Çünkü kaydetme olanağı yok. Kabul.
O eksikliği veri isminde bir sınıfla gidermek istiyoruz. O da kabul. Yani bu sınıfın yararı, yapılan her değişiklikte dosyanın da değişmesi oluyor, öyle mi?
Tabii veri'de bir eksiklik var: verilere nasıl erişileceğini hiç konuşmadık. Acaba perde arkasında bir eşleme tablosu kullanmak yararlı olur mu? Şöyle bir şey?
class veri
{
Kişi[string] veri;
/* ... başka üyeler */
void ekle(string anahtar, Kişi değer)
{
veri[anahtar] = değer;
kaydet();
}
Kişi eriş(string anahtar)
{
return veri[anahtar];
}
/* ... */
}
Yani verinin depolanmasının bir eşleme tablosu halleder, biz de değişikliklerde dosyayı baştan yazarız. (Sonuna eklemek olmuyor tabii ki; programı her çalıştırdığımızda dosya aynı kayıtlar tekrarlanarak büyüyor.)
Tabii şimdi de konu kaydet()'in nasıl yazılması gerektiğine geliyor. Kişi nasıl bir türdür? Bu sınıf her türle çalışmalı mıdır? (Bence her türle çalışmasını istemiyorsun. Bu her sütunun string olduğu bir tablo. Yani bunun üstünde bir soyutlama ile ilgilenmiyorsun.)
Bence en azından şöyle olmalı (bu fikirde kararlıyım :) : Bütün tablo, bellekte durmalı. Tablo, kullanıcıların veriEkle, satırBitti, vs. gibi işlevleri doğru çağırmasına dayanmamalı. Kullanıcılar bize tutarlı bir nesne vermeliler, biz onları depolamalıyız ve ancak ya kullanıcı söylediğinde, veya otomatik olarak dosyaya kaydetmeliyiz.
Örnek:
module veri;
import std.stdio;
import std.string;
import std.file;
class veri
{
string[][] kayıtlar;
string dosyaAdı;
this(string dosyaAdı)
{
this.dosyaAdı = dosyaAdı;
}
void ekle(string[] kayıt)
{
kayıtlar ~= kayıt;
/* Otomatik kayıt yerine kaydet()'i kullanıcının açıkça çağırmasını da
* isteyebiliriz */
kaydet();
}
void kaydet()
{
auto geciciDosyaAdı = dosyaAdı ~ "gecici";
auto dosya = File(geciciDosyaAdı, "w");
scope(failure) remove(geciciDosyaAdı);
/* ... burada kayıtlar yazılır; ben öylesine bir şey yazıyorum */
foreach (kayıt; kayıtlar) {
foreach (sütun; kayıt) {
dosya.writeln(sütun);
}
dosya.writeln();
}
rename(geciciDosyaAdı, dosyaAdı);
}
}
void main()
{
auto v = new veri("deneme.txt");
v.ekle(["ali", "ankara" ]);
v.ekle(["veli", "van"]);
/* Veya dinamik bir kayıt oluşturarak */
string[] kayıt;
kayıt ~= "zeynep";
kayıt ~= "zonguldak";
v.ekle(kayıt);
}
Orada scope'un da bir örneğini gösterdim. Geçici bir dosyaya yazıyoruz ve en sonunda geçici dosyanın ismini asıl dosyanın ismi olarak değiştiriyoruz.
Ama eğer bir hata olursa (yani "failure" durumunda), geçici dosya remove ile otomatik olarak silinecek. scope(failure)'ın yararı o.
Ali
--
[ Bu gönderi, http://ddili.org/forum'dan dönüştürülmüştür. ]