TROIA'da bir class, C#'taki gibi bir veri kutusu değil

C# veya Java'dan gelen biri için "class" dendiğinde akla önce alanlar (fields) ve property'ler gelir. TROIA'da bu beklenti kısmen yanlış çıkıyor: class'lar öncelikle davranış (method) koleksiyonlarıdır; nesnenin verisini tutmak için genelde TABLE tipi kullanılır. Belgelerin altını çizdiği bir başka önemli fark da şu: TROIA'da dialog'lar class değildir — UI ile davranış burada iki ayrı dünya.

İki özel metod: _VARIABLES ve _CONSTRUCTOR

Bir class instance'ı tanımlandığında iki özel metod otomatik olarak çalışır:

  • _VARIABLES: Üye değişkenleri (member) ve gerekli değişkenleri tanımlar.
  • _CONSTRUCTOR: Class'ın iç yapısını kurar.

Kritik detay: bu iki metod her instance için yalnızca bir kez çalışır — aynı instance'ı bir döngü içinde tekrar tekrar "tanımlıyor" gibi görünen kod yazsanız bile, _VARIABLES/_CONSTRUCTOR yeniden tetiklenmez. Bu, döngü içinde her seferinde "sıfırdan bir nesne" bekleyen bir mantık kurarsanız sizi şaşırtabilir.

Instance tanımlamak

OBJECT:
    MATEMATIKYARDIMCISI HESAPLAYICI;

Sözdizimi, ilkel bir tipin yerine class adının geldiği normal bir değişken tanımından farksız.

Metod çağırma

SONUC = HESAPLAYICI.TOPLA(5, 6);

Class'ın kendi içinde, kendi metodlarından birini çağırmak için THIS kullanılır — çünkü class'ı yazan geliştirici, o class'ın hangi instance adlarıyla kullanılacağını bilemez:

/* Class'ın içinde, bir metoddan başka bir metodu çağırmak */
SONUC = THIS.YARDIMCIMETOD(PARAM1);

Üye değişkenlere erişim: @ operatörü

STRUTIL@VARSAYILANAYIRICI = '-';

@ operatörü ile bir instance'ın üye (member) alanına erişilir. Önemli bir kısıtlama: @ operatörü sadece tek seviye için kullanılabilir — yani A@B@C gibi zincirleme bir erişim desteklenmez. İç içe class yapılarında bir üyenin üyesine ulaşmak istiyorsanız, ara adımı ayrı bir değişkende tutmanız gerekir.

Gerçek bir örnek: basit bir sayaç class'ı

/* Sayac class'ı - _VARIABLES ve _CONSTRUCTOR ile başlangıç durumu kurulur */

/* _VARIABLES metodu içinde: */
MEMBER:
    INTEGER DEGER;

/* _CONSTRUCTOR metodu içinde: */
DEGER = 0;

/* ARTIR adlı normal bir class metodu: */
PARAMETERS:
    INTEGER MIKTAR;

DEGER = DEGER + MIKTAR;
RETURN DEGER;

Kullanım:

OBJECT:
    SAYAC SAYACIM;

LOCAL:
    INTEGER GUNCELDEGER;

GUNCELDEGER = SAYACIM.ARTIR(5);
GUNCELDEGER = SAYACIM.ARTIR(3);
/* GUNCELDEGER artık 8 - aynı instance'ın state'i korunuyor */

Kalıtım: destekleniyor, ama bir istisnayla

TROIA class'ları kalıtımı ve metod override etmeyi destekliyor — ama initializer metodları (_VARIABLES, _CONSTRUCTOR) override edilemiyor. Bir alt sınıfta ek başlangıç mantığı gerekiyorsa, bunu ayrı, normal bir metod olarak tanımlayıp üst sınıfın constructor'ından ya da instance oluşturma sırasında ayrıca çağırmanız gerekir. (Inheritance/Cross konusunu ayrı bir yazıda daha derinlemesine ele alacağım.)

Sık yapılan hatalar

  • Nesnenin verisini class member'larında tutmaya çalışmak — belgelerin de vurguladığı gibi, gerçek CANIAS projelerinde iş nesnesi alanları genelde TABLE ile modellenir; class'ı bir "entity" gibi tasarlamak, platformun geri kalanıyla (SELECT, LOOP AT, SORT gibi tablo odaklı komutlarla) uyumsuz bir yapı ortaya çıkarır.
  • _VARIABLES/_CONSTRUCTOR'ın her çağrıda çalışacağını sanmak — bir döngüde aynı instance için tekrar "tanımlama" benzeri kod yazıp state'in sıfırlanmasını beklemek; state'i gerçekten sıfırlamak istiyorsanız bunun için ayrı bir RESET metodu yazmanız gerekir.
  • @ operatörünü zincirlemeye çalışmak — INSTANCE@ICMEMBER@ALTALAN gibi bir erişim beklemek derleme hatasına yol açar; ara adımı mutlaka ayrı bir değişkende tutun.
  • THIS yerine instance adını class'ın kendi içinde kullanmak — class'ı yazarken hangi değişken adıyla instantiate edileceğini bilemezsiniz; class içi çağrılarda her zaman THIS kullanmak, class'ı farklı instance adlarıyla yeniden kullanılabilir tutar.

Sıradaki adım

Kalıtımın TROIA'ya özgü ikinci yüzü olan Cross mekanizmasını (recompile olmadan davranış değiştirme) ve SUPER kullanımını bir sonraki yazıda ele alacağım.