Delphi’de Dokümantasyon Nasıl Yapılır

Created with Sketch.

Delphi’de Dokümantasyon Nasıl Yapılır

Elbette bir çok yol ile yapılabilir. Document Insight gibi ücretli veya HelpNDoc gibi kişisel kullanım için ücretsiz olan bazı araçlar da kullanabilirsiniz fakat yeteri kadar özen göstermekten uzaksanız, bunun için sadece Notepad bile işinizi görecektir. Benim hedef kitlem malesef bu yöntemi tercih edenler olamayacak 🙂

Espri bir yana, gelecekteki siz için veya kodunuzu okuyacak olan diğer iş arkadaşlarınız için bilgi notları bırakabileceğiniz bir mekanizmayı Delphi hali hazırda zaten destekliyor. Bu mekanizmayı kullanarak metodlarınız ve sınıflarınız için basit anlamda tanıtıcı bilgiler verebileceğiniz veya bu alt yapıyı kullanarak programcı kılavuzları hazırlayabileceğiniz bir mekanizma olduğunu biliyor muydunuz? Bu mekanizmanın adı “XML Documentation Comments” olarak literatüre geçmiş durumda…

Bu yazıda basit bir şekilde baştan sona bir dökümantasyon belgesini nasıl yapabileceğinizi göstermeye çalışacağım. Burada anlatılan teknikler Delphi’de tanımladığınız herşey için geçerlidir. Kapsamı ise son kullanıcılar değil, tamamen programcılar içindir. Dilin bir çok yapısını içinde barındırabildiği için özellikle bir sınıf üzerinde bunu örneklendirmeyi doğru buldum fakat anlaşılması zor bir hal alacağı için Record’larla yetinmeyi tercih ettim. Siz bir sabit, bir değişken, bir kayıt (record), bir sınıf, bir fonksiyon veya prosedür üzerinde de bunları tanımlayabilirsiniz. Tekniğin tamamı bunlar için de geçerlidir.

Öncelikle bu, özünde bir XML diyalektiğidir ve (tıpkı C#’da da olduğu gibi) Pascal kodları içinde XML taglarını doğrudan kullanamayacağımız için Dökümantasyona ait XML taglarının başına (tırnaklar hariç) “///” işaretlerini ekliyoruz. Ardından taksonomide geçen taglar aracılığıyla INTERFACE bölümünde dökümantasyonumuzu gerçekleştiriyoruz.

Bu arada, aynı taksonomiyi C# dilinde de kullanabilirsiniz, muhtemelen orada da döküman üretme aracı vardır. C#’da dökümantasyona ilgi duyan arkadaşlarım için aşağıdaki link bir başlangıç noktası olabilir;

https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/xmldoc/how-to-use-the-xml-documentation-features

Konuya dönecek olursak, basit bir örnek ile işe başlayalım. Normalde bir sabiti tanımlarken const ifadesinden sonra sabitin adını ve eşitliğin sağına da değerini yazıyoruz ve normalde aşağıdaki gibi gözüküyor;

const
  Pi = 3.14159;

Dökümantasyon ile ilgili taksonomiyi kullandığımızda ise kod, şu şekle bürünüyor;

const
  /// <summary>
  ///   Pi sayısını temsil eder.
  /// </summary>
  /// <value>
  ///   3.14159
  /// </value>
  /// <remarks>
  ///   Pi sayısı, bir dairenin çevresinin çapına bölümü ile elde edilen
  ///   irrasyonel matematik sabiti'dir. İsmini, Yunanca sözcüğünün ilk harfi
  ///   olan "?" den alır. Pi sayısı, Arşimet sabiti ve Ludolph sayısı olarak
  ///   da bilinir.
  ///  Aşağıdaki örnek kod bir radyanın kaç derece olduğunu hesaplar.
  ///  x := 1 * (180 / Pi);  /// </remarks>  /// <seealso href="https://tr.khanacademy.org/math/algebra2/trig-functions/intro-to-radians-alg2/v/we-converting-radians-to-degrees">  ///   Radyanı Dereceye Dönüştürme  /// </seealso>  Pi = 3.14159;

XML’e vakıfsanız yukarıdaki örnekte bazı tagların kullanıldığını fark etmişsinizdir. Sırasıyla bunların ne anlama geldiğini yazının devamında zaten inceleyeceğiz fakat ondan önce, bu kadar kodun pratikte bizim ne işimize yarayacağını da görsel olarak incelemekte de fayda var. RAD IDE’nin “Help Insight” özelliğini aktif ettiyseniz, fareyi bir identifier veya variable’nin üzerinde beklettiğinizde bir hint aracılığıyla o kod ile ilgili bir yardım baloncuğu gözükür. Tıpkı aşağıdaki gibi;

İşte bu taksonomi temelde bu işe yarıyor. Bunun faydaları üzerinde biraz düşünelim ve diğer faydalarına da göz atalım;

  • Eğer bir ekip halinde çalışıyorsanız bu özellik çok kritik bir değere sahip, ekip üyelerinin hepsinin aynı kod özelinde aynı şeyi anlamasına yardımcı olan bir araç olarak kendisini gösteriyor.
  • Bununla, ileriki zamanlara yönelik bir hatırlatma işlevi de kazanıyoruz. Yani geçmişte yazdığınız eski bir kod ile ilgili hatırlanması gereken kritik noktaları bu sayede sistemli bir hale getirmiş oluyoruz.
  • Model Viewer aracılığıyla HTML Help dosyası da üretebiliyorsunuz.
  • Eğer Documentation Insight gibi ücretli bir eklenti kullanıyorsanız, çeşitli formatlarda (HLP, CHM vs…) programcı kılavuzları da üretebiliyorsunuz.
  • Böyle bir yapıyı kullanarak eskiden yazdığınız kodları anlamakla zaman kaybetmiyorsunuz. (Sanırım en faydalısı da bu…)

Dökümantasyonda Kullanılacak Bazı Taglar;

Burada sadece tagların isimlerini ve anlamlarını paylaşacağım. Nasıl kullanıldıkları ile ilgili örneği ise yazının en sonunda topluca vereceğim. Uzunca bir kod olduğu için örnek bolluğu açısından doyurucu olduğunu söyleyebilirim.

summary

Hedef işlevin veya sınıfın özetini içermelidir. Buraya yazacağınız ifade kısa ve öz olmalı, herkesin anlayacağı basitlikte ve sade bir içeriğe sahip olmalıdır.

remarks

Hedef işlev veya sınıf hakkında genişçe bilgi verebileceğiniz, uzun uzadıya, detaylı açıklamaların ve anlatımların yer aldığı kısımdır.

param

Belirli bir parametrenin adı ve açıklamasını içeren bölümdür.

returns

Çıktı üreten bir yapı (mesela fonksiyon gibi bir şey) ise dönüş değeri ve açıklamasını buradan yapabilirsiniz.
Buraya kadar tanıtılan taglar Help Insight çerçevesinde fareyi ilgili kodun üzerinde beklettiğinizde karşınıza gelen bilgi baloncuğunda gösterilir. Aşağıdaki diğer taglar ise Model View aracılığıyla derlediğiniz dökümantasyon belgesinde kullanılmak üzere tasarlanmıştır. Yukarıdaki taglar günlük ihtiyacınızı karşılarken aşağıdaki taglar işi bir sonraki seviyeye taşıyan yapıyı ifade eder.

para

Bir paragrafı ifade eder. Diğer tagların içinde paragraf açmak için kullanılır.

c

Sabit karakter genişliğine sahip yazı tiplerini içeren bir metin bölgesini ifade eder. Bu metin bölgesinde kullanılan font “courier new“, “inconsolata“, “Fixedsys” gibi sabit harf genişliğine sahip bir yazı tipidir…

code

Örneklemeler yapabileceğiniz bir bölümdür. Burada verdiğiniz örneğin dilini “lang” attributesi ile belirtebilirsiniz.

see

Belirli bir tip, sembol veya tanımlayıcıya referans vermenizi, help dökümanında link tanımlamanızı sağlar. döküman içindeki bir elemana referans verirken “cref“, harici bir URL’ye link vermek için “href” attributesini kullanabilirsiniz.

exception

İstisna ve hata mesajları gibi konularda yapabileceğiniz açıklamaları içerir. Hatanın türü ve açıklamasını belirtmenizi sağlar.

permission

Bir yöntemin izinlerini anlatabileceğiniz bölümdür.

Dökümantasyondaki taglar elbette sadece bunlardan ibaret değil, bunun yanında daha bir çok tag mevcut. Diğer taglar ile ilgili daha ayrıntılı bilgi edinmek isteyenler için “XML Documentation for Delphi” aramasını google’da yapabilirsiniz…

Programcı Kılavuzu Nasıl Üretilir

Kapsamlı bir örneğe geçmeden önce, HTML Dökümanını nasıl üreteceğinize kısaca değinelim. Bunun için projenize Model View desteğini eklemelisiniz. Öncelikle menüden View \ Model View penceresini açmanız gerekir. Bu pencere aktif hale geldiğinde, size bu proje için model desteğini eklemek isteyip istemediğinizi soran bir mesaj gösterecektir. Soruya “Yes” diyerek model desteğini projenize ekleyebilirsiniz. Bundan sonrasında ise Model View penceresinde projenize sağ tıklayıp “Generate Documentation” butonuna basın. Karşınıza, dökümantasyonu nereye kaydedeceğinizi seçmenizi sağlayan bir ekran çıkacaktır. İşinize gelen ayarlamaları yaptıktan sonra “OK” butonuna bastığınızda varsayılan web browseriniz açılacak ve HTML dökümantasyonunuz gösterilecektir. Aşağıdaki ekran görüntüleri bu süreci anlamanızı kolaylaştırabilir;

 

Bu adımların sonucunda ise oluşan HTML dokümanı aşağıdaki gibi gözükecektir;

Pek sevimli bir görünüm olmasa da ufak bir CSS ayarlamasıyla yüzüne bakılabilir bir dokümantasyona sahip olabilirsiniz. Yetmediyse Document Insight ile oluşturulmuş örnek bir kılavuz belgesi ise aşağıdaki gibidir;

Örnek

Genel geçer tagları tanıdığımıza ve basitçe dökümantasyon dosyasını nasıl üreteceğimizi öğrendiğimize göre, artık kapsamlı bir örnek ile konuyu bağlayabiliriz.

unit Ana_;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Generics.Collections, REST.Json;

const
  /// <summary>
  ///   Pi sayısını temsil eder.
  /// </summary>
  /// <value>
  ///   3.14159
  /// </value>
  /// <remarks>
  ///   Pi sayısı, bir dairenin çevresinin çapına bölümü ile elde edilen
  ///   irrasyonel matematik sabiti'dir. İsmini, Yunanca sözcüğünün ilk harfi
  ///   olan "?" den alır. Pi sayısı, Arşimet sabiti ve Ludolph sayısı olarak
  ///   da bilinir.
  ///  Aşağıdaki örnek kod bir radyanın kaç derece olduğunu hesaplar.
  ///  x := 1 * (180 / Pi);
  /// </remarks>
  /// <seealso href="https://tr.khanacademy.org/math/algebra2/trig-functions/intro-to-radians-alg2/v/we-converting-radians-to-degrees">
  ///   Radyanı Dereceye Dönüştürme
  /// </seealso>  Pi = 3.14159; type
  /// <summary>
  ///   Bireylerin kimlik özelliklerinin tanımlandığı yapı.
  /// </summary>
  /// <remarks>
  ///   Bireyleri kimlik açısından tanımlayan örnek bir Record tipidir. TÜZEL
  ///   Kişiler yani şirketleri tanımlamak için lütfen TTuzel tipindeki yapıyı
  ///   kullanın.
  /// </remarks>
  /// <example>
  ///     /// var
  ///   aKisi: TSahis;
  /// begin
  ///   with aKisi do begin
  ///        Ad    := 'mehmet akif';
  ///        Soyad := 'ersoy';
  ///        Yil   := 1898;
  ///        ShowMessage(Kunye);
  ///   end;
  /// end;
  ///    /// </example>
  TSahis = record
   private    
/// <seealso cref="Ad">    
 ///   Ad alanına bakınız.    
 /// </seealso>
     FAd     : String;    
 /// <seealso cref="Soyad">    
 ///   Soyad alanına bakınız.    
 /// </seealso>
     FSoyad  : string;    
 /// <seealso cref="Yil">    
 ///   Yil alanına bakınız.    
 /// </seealso>
     FYil    : Integer;    
 /// <summary>    
 ///   Adların ilk harfini büyük devamını küçük olacak şekilde FAd    
 ///   değişkenine aktarılmasını sağlar    
 /// </summary>    
 /// <param name="Value">    
 ///   Ad listesini temsil eder. Ad listesi boşluk CHR(32) ile    
 ///   bölüştürülür.    
 /// </param>    
 /// <seealso cref="Ad">    
 ///   Ad alanını inceleyiniz.    
 /// </seealso>
     procedure SetAd(const Value: String);    
 /// <summary>    
 ///   Soyadındaki tüm harfleri büyük harfe çevirir FSoyad değişkenine    
 ///   aktarır.    
 /// </summary>    
 /// <param name="Value">    
 ///   Soyadı listesini temsil eder. Soyadları boşluk CHR(32) ile    
 ///   bölüştürülür.    
 /// </param>
   procedure SetSoyad(const Value: string);    
 /// <summary>    
 ///   Bireyin doğum yılının kurallara uygun şekilde belirtilmesini    
 ///   sağlar.    
 /// </summary>    
 /// <param name="Value">    
 ///   Doğum yılını ifade eder.    
 /// </param>    
 /// <remarks>    
 ///   Doğum yılı ile ilgili mantıksal kuralların işletilmesini sağlar.    
 ///   Doğum yılı bugünün tarihinden büyük olamaz. Yani kişi gelecek yıl    
 ///   doğamaz.    
 /// </remarks>
     procedure SetYil(const Value: Integer);    
 /// <summary>    
 ///   Türk Harflerine göre verilen metni küçük ya da büyük harfe çevirir.    
 /// </summary>    
 /// <param name="aString">    
 ///   Dönüştürülecek olan metni ifade eder.    
 /// </param>    
 /// <param name="aBuyukBasHarf">    
 ///   İlk harfin büyük olup olmadığını belirler.    
 /// </param>    
 /// <param name="aTumuBuyuk">    
 ///   Tüm harflerin büyük olup olmayacağını belirler.    
 /// </param>    
 /// <remarks>    
 ///   Performansı arttırmak için fonksiyon içindeki alfabe harflerin    
 ///   kullanım yoğunluğuna göre sıralanmıştır. Buna göre en çok    
 ///   kullanılan harfler alfabenin başında, en az kullanılanlar ise en    
 ///   sonunda yer alır.    
 /// </remarks>
     function StringCaseTR(aString: String; aBuyukBasHarf: Boolean = True; aTumuBuyuk: Boolean = False): String;
   public    
 /// <summary>    
 ///   Bireyin ilk adını ve ikinci adını tutar    
 /// </summary>    
 /// <value>    
 ///   'Uğur Göksel'    
 /// </value>    
 /// <remarks>    
 ///   Kişinin adını tutar, veri girişi sırasında ad alanının biçimsel    
 ///   olarak formatlanmasını yani ilk harfin büyük devamının ise küçük    
 ///   olmasını sağlar. Her boşluktan sonraki ilk harfi büyütülür...    
 /// </remarks>    
 /// <seealso cref="Isim">    
 ///   Isim fonksiyonunu inceleyiniz.    
 /// </seealso>
     property Ad   : String    read FAd      Write SetAd   ;    
 /// <summary>    
 ///   Bireyin soy adını ifade eder.    
 /// </summary>    
 /// <value>    
 ///   'PARLAYAN'    
 /// </value>    
 /// <remarks>    
 ///   Evli hanımlar için evlenmeden önceki soyadı eşinin soyadından önce    
 ///   belirtilmelidir. Veri girişi sırasında tüm soyadı değeri büyük    
 ///   harfe dönüştürülür. Bu anlamda boşluk karakteri herhangi bir engel    
 ///   teşkil etmez.    
 /// </remarks>    
 /// <seealso cref="Isim">    
 ///   Isim fonksiyonunu inceleyiniz.    
 /// </seealso>
     property Soyad: string    read FSoyad   write SetSoyad;    
 /// <summary>    
 ///   Bireyin doğduğu yılı gösterir.    
 /// </summary>    
 /// <value>    
 ///   1976    
 /// </value>    
 /// <exception cref="Exception">    
 ///   Doğum yılı bugünden büyük olamaz.    
 /// </exception>    
 /// <seealso cref="Yas">    
 ///   Yas fonksiyonunu inceleyiniz.    
 /// </seealso>
     property Yil  : Integer   read FYil     write SetYil  ;    
 /// <summary>    
 ///   Bireyin şu anki yaşını hesaplar.    
 /// </summary>    
 /// <returns>    
 ///   2018 yılında olduğumuzu varsayarsak sonucu 42 olarak geri döndürür.    
 /// </returns>    
 /// <remarks>    
 ///   Bilgisayarın gösterdiği tarihin yıl bilgisinden kişinin "Yil"    
 ///   alanında belirtilen bilgiyi çıkararak hesaplama yapar.    
 /// </remarks>    
 /// <seealso cref="Yil">    
 ///   Yil alanını inceleyiniz.    
 /// </seealso>      function Yas  : Integer; overload;    
 /// <summary>    
 ///   Ad ve Soyad bilgisini birlikte verir.    
 /// </summary>    
 /// <returns>    
 ///   'Uğur PARLAYAN'    
 /// </returns>    
 /// <remarks>    
 ///   Formatlanmış bir şekilde bireyin ad ve soyadını birlikte verir.    
 /// </remarks>    
 /// <seealso cref="Ad">    
 ///   Ad alanını inceleyiniz.    
 /// </seealso>    
 /// <seealso cref="Soyad">   
 ///   Soyad alanını inceleyiniz.    
 /// </seealso>
     function Isim : String;    
 /// <summary>    
 ///   Bireyin isim ve yaş bilgisini birlikte verir.    
 /// </summary>   
 /// <returns>    
 ///   'Uğur PARLAYAN 42 yaşındadır'    
 /// </returns>    
 /// <remarks>    
 ///   Bireyin adı, soyadı ve doğum yılı bilgisini kullanarak formatlı bir    
 ///   şekilde (Returns kısmındaki) gibi gösterilmesini sağlar.    
 /// </remarks>
     function Kunye: String;
 end;
 TAna = class(TForm)
   BT_TEST: TButton;
   procedure BT_TESTClick(Sender: TObject);
 private
   { Private declarations }
 public
   { Public declarations }
 end;
var  Ana: TAna;
implementation
{$R *.dfm}
uses  System.DateUtils;
{ TSahis }
function TSahis.Isim: String;
begin
 Result := String(FAd + ' ' + FSoyad).Trim;
end;
function TSahis.Kunye: String;
begin
 Result := Format('%s %d yaşındadır.', [Isim, Yas]);
end;
function TSahis.StringCaseTR(aString: String; aBuyukBasHarf: Boolean; aTumuBuyuk: Boolean): String;
const
 K = 'aeirlıdknmyustboüşzghçğcvpöfjwx';
  // Harfleri böyle sıralanmasının sebebi
 B = 'AEİRLIDKNMYUSTBOÜŞZGHÇĞCVPÖFJWX';
  // Bunların Türkçedeki kullanım sıklıklarına göredir.
var
 I: Integer;                              // Sayaç, indis değişkeni
 X: Integer;                              // Bulunan harfin alfabedeki indis numarası
begin
 Result := aString;
 if (aTumuBuyuk = False) then begin
     for I := 1 to Result.Length do begin
    // metni harf harf geziyoruz. (Bir rakam ile de karşılaşabiliriz...)
         X := pos(Result[I], B);              // POS, büyük / küçük harfe duyarlıdır (Case Sensitive). Yani büyükse küçüğü, küçükse büyüğü bulmaz.
         if (X > 0) then Result[I] := K[X];   // Bulunan karakter alfabemizde varsa küçüğüyle yer değiştiriyoruz.
     end;
     if (aBuyukBasHarf = True) then
         for I := 1 to Result.Length do begin // Metni harf harf geziyoruz
             X := pos(Result[I], K);
             if (X > 0) then begin
                 Result[I] := B[X];
                 Exit;                        // İlk harfi büyüttükten sonra çıkıyoruz...
             end;
         end;
 end else begin
     for I := 1 to Result.Length do begin     // metni harf harf geziyoruz. (Bir rakam ile de karşılaşabiliriz...)
         X := pos(Result[I], K);              // POS, büyük / küçük harfe duyarlıdır (Case Sensitive). Yani büyükse küçüğü, küçükse büyüğü bulmaz.
         if (X > 0) then Result[I] := B[X];   // Bulunan karakter alfabemizde varsa küçüğüyle yer değiştiriyoruz.
     end;
 end;
end;
procedure TSahis.SetAd(const Value: String);
var
 Tmp: TStringList;
 I: Integer;
 S: string;
begin
 Tmp := TStringList.Create;
 Tmp.Delimiter := #32;
 Tmp.DelimitedText := Value;
     for I := 0 to Tmp.Count - 1 do begin
     S := LowerCase(Tmp.Strings[I]).Trim;
     if S.IsEmpty = False then S[1] := stringCaseTR( Copy(S, 1, 1), TRUE) [1];
     Tmp.Strings[I] := S;
 end;
 FAd := Tmp.DelimitedText;
 FreeAndNil(Tmp);
end;
procedure TSahis.SetYil(const Value: Integer);
begin
 if (Value <= YearOf(Now))
 then FYil := Value
 else raise Exception.Create('Doğum yılı bugünden büyük olamaz.');
end;
function TSahis.Yas: Integer;
begin
 Result := YearOf(Now) - FYil;
end;
procedure TSahis.SetSoyad(const Value: string);
begin
 FSoyad := StringCaseTR(Value.Trim, True, True);
end;
{ TAna }
procedure TAna.BT_TESTClick(Sender: TObject);
var
  aKisi: TSahis;
begin
 with aKisi do begin
      Ad    := 'mehmet akif';
      Soyad := 'ersoy';
      Yil   := 1898;
      ShowMessage(Kunye);
 end;
 ShowMessage( Pi.ToString );
end;
end.

Sade bir Görünüm için Code Folding

Yukarıdaki örnek kod, dokümantasyon ile birlikte çok karmaşık, iç içe geçmiş gibi bir izlenim verebilir, alışkın olmayan gözler için fazla kalabalık gözükebilir. Bu sorunu aşmak için ise IDE’nin Code Folding özelliğinden faydalanabilirsiniz. Böyle bir durumda, yani daha sade bir görünüm için aşağıdaki ekran görüntüsünü inceleyebilirsiniz;

Faydalı olması dileğiyle,

 

Yorum yapılmamış

Yorumunuzu ekleyin