Component'i "mini dialog" gibi düşünmek yanlış olmaz, ama bir istisnayla

troia-platformu-nasil-calisir yazısında component'lerden, TROIA'da fonksiyon/modül import etme kavramı olmadığı için kod tekrarını önlemenin pratik yollarından biri olarak bahsetmiştim. Şimdi bunu biraz daha derinlemesine ele alalım: component'ler, teknik olarak dialog'larla aynı altyapıyı (SYSDIALOGS, SYSDLGTEXTS, SYSDLGCODES, SYSDLGFUNCTEX, SYSCONTROLS, SYSCTLTEXTS) paylaşıyor — kontrolleri, kontrol olaylarını ve metodları olan bir yapı olması bunu mantıklı kılıyor. Ama davranış olarak dialog'dan ayrıştığı kritik bir nokta var: kapsülleme (encapsulation).

Component'in değişkenleri dışarıya kapalı

/* Bu, dialog kodundan ÇALIŞMAZ: */
DEGER = COMPONENT1@ICDEGER;

Belgelerin net ifadesiyle: bir component içinde tanımlanan değişkenlere, dialog kodundan doğrudan erişmek (okumak ya da yazmak) mümkün değil. Bunu yapmak için component'in public metodlarını çağırmanız gerekiyor — yani component'i gerçek anlamda bir "kara kutu" gibi tasarlamanız bekleniyor.

/* Component'in dışarıya açtığı bir metod üzerinden değer set etmek */
C1.SETVALUES('01.01.2016 10:10:10', '01.02.2017 10:10:10');

Bu kısıtlama ilk bakışta can sıkıcı gelebilir ama aslında iyi bir tasarım disiplinini zorluyor: component'i kullanan dialog, component'in iç yapısını bilmek zorunda kalmıyor, sadece public arayüzünü (get/set metodlarını) biliyor. Bu, .NET'teki private alan + public property mantığının TROIA'daki karşılığı gibi düşünülebilir.

Component içinde component: iç içe kullanım

/* Component A'nın içine Component B'yi yerleştirmek */

Belgeler bunun "bir component'i dialog üzerinde kullanmakla tamamen aynı süreç" olduğunu belirtiyor — araç kutusundan bir TROIA Component sürükleyip, Component özelliğinde iç component'in adını belirtiyorsunuz. Bu, karmaşık bir arayüzü (örneğin bir "adres" component'inin içinde bir "il/ilçe seçici" component'i) katmanlı şekilde inşa etmeyi mümkün kılıyor.

Component'ten dialog çağırmanın kapsam etkisi

Şaşırtıcı bir detay: bir component içinden bir dialog çağırırsanız, o dialog'da tanımlanan tüm değişkenler (kod ya da kontrol sembolleriyle) component'in kapsamına dahil olur. Yani component → dialog çağrısı, dialog'u component'in bir uzantısı gibi kapsam içine alıyor; bu davranışı bilmeden, çağrılan dialog'daki bir değişken adının component'teki bir değişkenle çakışması sürpriz bir etkileşime yol açabilir.

Component olaylarını tetiklemek: FIRECOMPONENTEVENT

FIRECOMPONENTEVENT {olayadi};

Bir component'in kendi tanımlı olaylarından birini programatik olarak tetiklemenizi sağlıyor — kullanıcı etkileşimi beklemeden, kod akışının component'in olay mantığını çalıştırmasını istediğinizde kullanılır.

Gerçek bir örnek: tarih aralığı seçen bir component

/* Component'in public SETVALUES metodu, iç ACTIVEROW üzerinden değerleri günceller. */
PARAMETERS:
    DATETIME BASLANGIC,
    DATETIME BITIS;

MEMBER:
    /* Bu üye değişkenlere dialog kodundan doğrudan erişilemez */
    DATETIME ICBASLANGIC;
    DATETIME ICBITIS;

ICBASLANGIC = BASLANGIC;
ICBITIS = BITIS;

CONTROL1_TEXT = FORMATDATE(ICBASLANGIC, 'dd.MM.yyyy');
CONTROL2_TEXT = FORMATDATE(ICBITIS, 'dd.MM.yyyy');

Dialog tarafında kullanım:

TARIHSECICI1.SETVALUES('01.01.2026 00:00:00', '31.01.2026 23:59:59');

Sık yapılan hatalar

  • @ operatörüyle component'in üye değişkenine dışarıdan erişmeye çalışmak — bu desteklenmiyor; ihtiyacınız olan veriyi component'in yazdığınız bir public getter metoduyla almanız gerekiyor.
  • Component'i "sadece görsel bir gruplama" gibi tasarlayıp state'i dışarıda tutmak — bu, component'in asıl avantajı olan kapsülleme ve yeniden kullanılabilirliği baştan feda etmek demektir; state component'in içinde kalmalı, dışarıya sadece davranış (metod) açılmalı.
  • Component içinden çağrılan dialog'daki değişken adlarını component'in kendi değişkenleriyle çakıştırmak — kapsamın birleştiğini bilmeden aynı adı iki yerde kullanmak, beklenmedik değer üzerine yazmalara yol açabilir.
  • Derin iç içe component yapılarını abartmak — component içinde component içinde component şeklinde çok katmanlı bir yapı, teknik olarak mümkün olsa da hata ayıklamayı zorlaştırır; her katmanın gerçek bir yeniden kullanım değeri olduğundan emin olun.

Sıradaki adım

Kalıtımın ikinci yüzü olan Cross mekanizmasına (recompile olmadan davranış değiştirme) ve SUPER'e bir sonraki yazıda değineceğim.