İki farklı "değiştirme" mekanizması, iki farklı zamanlama

troiada-siniflar yazısında class kalıtımını desteklendiğini ama initializer metodlarının (_VARIABLES/_CONSTRUCTOR) override edilemediğini söylemiştim. Bu yazıda kalıtımın geri kalanına ve TROIA'ya özgü ikinci, çok daha ilginç bir mekanizmaya (Cross) bakacağız. İkisi arasındaki temel fark şu: inheritance derleme zamanında çözülür, Cross ise çalışma zamanında.

Tekli kalıtım, zincirlemeye izin var

TROIA'da bir alt sınıfın yalnızca tek bir üst sınıfı olabilir — ama üst sınıfın da kendi üst sınıfı olabilir (A→B→C şeklinde zincirleme). Kalıtım class, dialog, rapor ve component için destekleniyor. Belgelerin altını çizdiği pratik bir kural: davranışı/yapıyı gerçekten değiştirmek istiyorsanız, en az bir metodu override etmeniz ya da yeni bir metod eklemeniz gerekiyor — sadece kalıtım almak, hiçbir şeyi otomatik değiştirmiyor.

SUPER(): üst sınıfın kodunu birleştirmek

SUPER();
CLEAR ALL T1;

SUPER(), derleme zamanında çalışan bir yapı — üst sınıfın override edilen metodunun kodunu, çağıran metodun içine birleştiriyor. Belgelerdeki en kritik uyarı burada: üst sınıfın metodunda bir RETURN varsa, TROIA yorumlayıcısı override eden metodun geri kalan kodunu görmezden geliyor. Yani üst sınıfın metodu bir noktada RETURN yapıyorsa ve siz SUPER()'ı o metodun başında çağırdıysanız, kendi eklediğiniz kodun hiçbir zaman çalışmayacağı bir durumla karşılaşabilirsiniz — bu, override ettiğiniz metodun içeriğini önceden okumadan SUPER() kullanmanın riskli olmasının sebebi.

SUPER nesnesi: dönüş değeri alabilen alternatif

v5.01.07 sonrası, sadece class'larda kullanılabilen bir alternatif geldi:

R = SUPER.M1(PARAM);

Bu, SUPER()'ın "kod birleştirme" davranışından farklı olarak, üst sınıfın metodunu normal bir fonksiyon çağrısı gibi çağırıp dönüş değerini almanızı sağlıyor — yukarıda bahsettiğim RETURN sorununu da bypass ediyor, çünkü artık kod birleştirmiyorsunuz, gerçek bir çağrı yapıyorsunuz.

Cross: recompile olmadan davranış değiştirmek

troia-platformu-nasil-calisir yazısında Cross'tan kısaca bahsetmiştim; şimdi mekanizmanın kendisine bakalım. Cross, "her nerede A sınıfı tanımlıysa, sistem onun yerine B sınıfını yükler" diyen, veritabanında saklanan bir kayıt — derleme zamanında değil, çalışma zamanında etkili oluyor. Bu, örneğin standart bir CANIAS transaction'ını, kaynak kodunu hiç değiştirmeden, müşteriye özel bir davranışla değiştirmenin yolu.

Cross yükleme sırası: kim kimi eziyor

Öncelik sırası (en yüksekten en düşüğe):

  1. Kullanıcının kendi cross'ları
  2. Kullanıcı profili cross'ları (en derinden başlayarak)
  3. Sistem cross'ları

Belgelerin özeti net: "en son yüklenen cross, öncekini geçersiz kılar." Bu, bir kullanıcı için beklenmedik bir davranış gördüğünüzde, teşhis sırasını da belirliyor: önce o kullanıcıya özel cross var mı, sonra bağlı olduğu profillerde (en derinden başlayarak), en son sistem cross'unda bakmalısınız.

Nerede tanımlanır

  • Class'lar için: DEVT08 - System Cross, SYSCLSREF tablosunda saklanıyor.
  • Dialog/Rapor/Component için: DEVT09, SYSDLGREF tablosunda saklanıyor.
  • Kullanıcı/Profil seviyesi için: SYST03 transaction'ı, IASUSERCLSREF ve IASUSERDLGREF tablolarını kullanıyor.

Gerçek bir örnek: bir siparişi override etmek

Standart bir SIPARISHESAPLA class'ının bir müşteri için farklı bir iskonto mantığıyla çalışmasını istiyorsanız: MUSTERIYE_OZEL_SIPARISHESAPLA adlı yeni bir class yazıp, SIPARISHESAPLA'dan miras alıp ilgili metodu override edersiniz. Sonra bu iki class arasında sistem seviyesinde bir Cross tanımlarsanız, kaynak kodda SIPARISHESAPLA çağıran hiçbir yeri değiştirmeden, sistem çalışma zamanında otomatik olarak yeni class'ı kullanır. Bu değişikliği yalnızca belirli bir kullanıcı/profil için istiyorsanız, aynı cross'u sistem yerine kullanıcı/profil seviyesinde tanımlarsınız.

Sık yapılan hatalar

  • SUPER()'ı, üst metodun içeriğini incelemeden çağırmak — üst metodda erken bir RETURN varsa, kendi eklediğiniz kod hiç çalışmayabilir; SUPER() kullanmadan önce üst sınıfın metodunu mutlaka okuyun.
  • Dönüş değeri gereken bir senaryoda SUPER() kullanmak — dönüş değerine ihtiyacınız varsa (ve class içindeyseniz) SUPER.METOD() nesne sözdizimini tercih edin, SUPER()'ın kod birleştirme davranışı bu senaryoya uygun değil.
  • Bir cross'un hangi seviyede tanımlı olduğunu bilmeden davranış değişikliğini "koddaki bir hata" sanmak — beklenmedik bir class/dialog davranışı gördüğünüzde, kaynak koddan önce kullanıcı/profil/sistem cross tanımlarını kontrol etmek zaman kazandırır.
  • Çakışan birden fazla cross tanımlamak ve hangisinin kazanacağını tahmin etmeye çalışmak — öncelik sırası nettir (kullanıcı > profil > sistem); bu sırayı bilmeden birden fazla seviyede aynı class için cross tanımlamak kafa karıştırıcı olabilir.

Sıradaki adım

Transaction'ları programatik olarak çağırmayı (CALL TRANSACTION ... WITH WAIT, INSERVER) bir sonraki yazıda ele alacağım.