Decorator Design Pattern

Giriş

Bu makalede; sık kullanılan Design Pattern’lardan biri olan Decorator’ı tanıyacağız.

Bu Pattern, size SAP’nin BADI teknolojisi üzerinden tanıdık gelebilir. Şimdiye kadar BADI’lerin içine kod yazmış olabilirsiniz. Bu yazının sonunda, uygulamalarınızı BADI mantığında genişletebilir hale gelmenizi hedefliyoruz.

Ön Koşullar

Bu makalede, aşağıdaki konulara aşina olduğunuz varsayılmaktadır.

  • ABAP bilgisi
  • Object Oriented ABAP tecrübesi
  • Interface ve Class kavramları
  • Casting

Decorator Nedir?

Decorator Design Pattern; aynı veri üzerinde birden fazla değişiklik yapılması gerektiği durumlarda kullanılır. Bu değişikliklerin tamamını ana programa veya merkezi bir sınıfa kodlamak yerine; dağınık haldeki mikro sınıflara kodlama prensibine dayanır.

Bu sayede; yeni bir kod eklemek gerektiğinde, test edilmiş eski kodlara herhangi bir şekilde dokunmadan yeni bir sınıf yazmak yeterli olmaktadır. Veya mevcut bir işlevi çıkarmak gerektiğinde, o işlevi barındıran sınıfı etkisiz hale getirmek yeterlidir.

Örnek Uygulama: User Exit

Decorator mantığını anlamak ve avantajlarını görmek için, tipik uygulama alanlarından biri olan User Exit örneğini inceleyeceğiz.

Aşağıdaki fonksiyonun bir User Exit olduğunu varsayalım.

FUNCTION ornek_user_exit
  IMPORTING
    !iv_bukrs TYPE bukrs
    !iv_waers TYPE waers
  TABLES
    !ct_data STRUCTURE ztt_data.

INCLUDE zxornek.

ENDFUNCTION.

Bu User Exit içerisinde 3 ayrı iş yapılacağını düşünerek, konuyu irdelemeye başlayalım.

Klasik ABAP Uygulaması

Geleneksel ABAP anlayışında, ZXORNEK dosyası içerisinde bu 3 iş alt alta ve ayrı ayrı kodlanacaktır.

*&----------------
*& Include ZXORNEK
*&----------------

" Exit'te yapılacak 1. iş buraya kodlanır
" Exit'te yapılacak 2. iş buraya kodlanır
" Exit'te yapılacak 3. iş buraya kodlanır

Bu yaklaşımın pek çok sakıncası vardır; bunlardan bazılarını dile getirelim.

  • Bu Include içerisinde kodlar, ister istemez birbirini etkileyecektir. Örneğin üst satırlara yazılmış bir EXIT komutu, alt satırlarda çalışması gereken bir kodun hiç çalışmamasına yol açabilir.
  • ZXORNEK içerisine 4. bir ek yapıldığında, diğer 3 işe ait senaryoların da tekrar test edilmesi gerekir.
  • ZXORNEK üzerinde farklı ABAP’çılar paralel çalışamaz.
  • ZXORNEK altındaki farklı işler farklı Request’lere dahil edilip canlıya ayrı ayrı taşınamaz. Parçalı taşıma gerektiğinde, kodların yıldızlanıp taşınıp tekrar açılması gibi zahmetli ve riskli yaklaşımlar gerekir.
  • ZXORNEK’e kodlar eklendikçe, bu dosya zaman içerisinde uzun ve anlaşılması zor hale gelir, bakım maliyeti ve hata riski gittikçe artar.

Şimdi aynı uygulamayı Decorator Design Patern ile yapıp, bu dezavantajları ortadan nasıl kaldıracağımızı görelim.

Decorator Adım 1: Interface

Çözümümüzün ilk adımı, bu User Exit için bir Interface oluşturmaktır. SAPGUI üzerinde SE24 işlem koduna giderek veya Eclipse içerisinde New → ABAP Interface menüsünü kullanarak Interface tanımlayabiliriz.

INTERFACE zif_decorator PUBLIC.

METHODS decorate
  IMPORTING
    !iv_bukrs TYPE bukrs
    !iv_waers TYPE waers
  CHANGING
    !ct_data TYPE ztt_data.

DECORATE yordamındaki parametrelerin, User Exit parametreleri ile örtüştüğüne dikkat edin.

Basit olması açısından, Interface’imiz içerisinde; hata yönetimi gibi ek işlevleri devre dışı bıraktık.

Decorator Adım 2: Uygulama Sınıfları

Bu adımda, 3 farklı işi yapacak 3 bağımsız sınıf yazacağız. SAPGUI üzerinde SE24 işlem koduna giderek veya Eclipse içerisinde New → ABAP Class menüsünü kullanarak sınıf tanımlayabiliriz.

CLASS zcl_decorator01 DEFINITION
  PUBLIC
  FINAL
  CREATE PUBLIC.

  PUBLIC SECTION.
    INTERFACES zif_decorator.
  PROTECTED SECTION.
  PRIVATE SECTION.

ENDCLASS.

CLASS zcl_decorator01 IMPLEMENTATION.

  METHOD zif_decorator~decorate.

    MODIFY ct_data
      FROM VALUE t_data( yabanci_kur = abap_true )
      TRANSPORTING yabanci_kur
      WHERE waers NE iv_waers.

  ENDMETHOD.

ENDCLASS.

Sınıf ZIF_DECORATOR Interface’ini uygulayıp, hayali tablomuzdaki YABANCI_KUR alanını doldurmuş olduk.

Interface’i uygulamamız, bu sınıfın ZIF_DECORATOR içerisinde geçen Method’ları barındırdığını sisteme garanti etmemiz anlamına geliyor. Sistemde ZIF_DECORATOR’u kullanacak şekilde yazılmış herhangi bir program, ZCL_DECORATOR01 sınıfının detaylarını bilmeye ihtiyaç duymadan bu sınıftan faydalanabilir. Zira; içerisinde DECORATE diye bir Method oluğu biliniyor.

Nasıl faydalanacağını birazdan göreceğiz.

Yine basit olması açısından, sınıf içerisindeki kodu basit tuttuk. Gerçek dünyada; bu sınıf içerisinde Type’lar, Private Method’lar, hata yönetimi, vb görmeyi beklerdik.

Aynı mantığı takip ederek, User Exit’te yapmamız gereken ikinci işi barındıran ikinci bir sınıf açıyoruz. Normalde bu sınıfların yapacağı işler daha karmaşık olacaktır, ancak örneği basit tutmak adına işleri de basit tutuyoruz.

CLASS zcl_decorator02 DEFINITION
  PUBLIC
  FINAL
  CREATE PUBLIC.

  PUBLIC SECTION.
    INTERFACES zif_decorator.
  PROTECTED SECTION.
  PRIVATE SECTION.

ENDCLASS.

CLASS zcl_decorator02 IMPLEMENTATION.

  METHOD zif_decorator~decorate.

    CHECK zcl_fi_toolkit=>is_company_special(
        iv_bukrs
      ) eq abap_true.

    MESSAGE e300(zfi) WITH iv_bukrs.

  ENDMETHOD.

ENDCLASS.

Dikkat ederseniz; bu sınıfta CHECK komutunu çekinmeden kullanabiliyoruz. CHECK, olsa olsa ZCL_DECORATOR02’den çıkmamız anlamına gelir. Diğer uygulama sınıflarını etkilemeyecektir.

Şimdi son sınıfımızı da kodlayalım.

CLASS zcl_decorator03 DEFINITION
  PUBLIC
  FINAL
  CREATE PUBLIC.

  PUBLIC SECTION.
    INTERFACES zif_decorator.
  PROTECTED SECTION.
  PRIVATE SECTION.

ENDCLASS.

CLASS zcl_decorator03 IMPLEMENTATION.

  METHOD zif_decorator~decorate.

    DELETE ct_data WHERE xflag EQ abap_false.
    CHECK ct_data IS INITIAL.
    MESSAGE e301(zfi).

  ENDMETHOD.

ENDCLASS.

Bu şekilde, basitleştirilmiş örnek sınıflarımızı tamamlamış oluyoruz. Elimizde bir Interface ve üç Class var.

Decorator Adım 3: User Exit Uygulaması

Bu son adımda; dağınık Lego parçaları gibi hazırladığımız bileşenleri User Exit içerisinde bir araya getireceğiz.

*&-----------------
 *& Include ZXORNEK
 *&----------------

DATA:
  lo_decorator TYPE REF TO zif_decorator,
  lo_object TYPE REF TO object.

" ZIF_DECORATOR Interface'ini uygulamış sınıfları tespit et

SELECT clsname
  FROM seometarel
  WHERE refclsname EQ 'ZIF_DECORATOR'
  INTO TABLE @data(lt_class).

" Her bir sınıfa ait nesneyi dinamik yaratıp çalıştır

LOOP AT lt_class ASSIGNING FIELD-SYMBOL(<ls_class>).

  CREATE OBJECT lo_object TYPE (<ls_class>-clsname).
  lo_decorator ?= lo_object.

  lo_decorator->decorate(
    EXPORTING
      iv_bukrs = iv_bukrs
      iv_waers = iv_waers
    CHANGING
      ct_data = ct_data[]
  ).

ENDLOOP.

Gördüğünüz gibi; Exit’in yaptığı iş, ZIF_DECORATOR Interface’ini uygulayan sınıfları tespit edip çağırmaktan ibaret. Exit içerisinde fonksiyonel herhangi bir kod yok.

Exit çalıştığında;

  1. SEOMETAREL tablosundan ZCL_DECORATOR01 & 02 & 03 sınıflarına erişilecektir.
  2. Loop’un ilk adımında, ZCL_DECORATOR01 içindeki DECORATE Method’u çalışacaktır.
  3. Loop’un ikinci adımında, ZCL_DECORATOR02 içindeki DECORATE Method’u çalışacaktır.
  4. Loop’un üçüncü adımında, ZCL_DECORATOR03 içindeki DECORATE Method’u çalışacaktır.

Inteface’i uygulayan sınıfları bulan kodu basit tutarak doğrudan SEOMETAREL tablosuna baktık. Bu basit kod, Abstract Class uygulandığı durumda sınıfları gözden kaçırabilir veya sınıfların sıralı çağırılması gereken durumda yeterli olmayabilir. Bu konudaki alternatif yaklaşımlar, örnek kodlarla birlikte Design Patterns in ABAP Objects adlı kitabımda var.

Değerlendirme

Bu yaklaşım, klasik ABAP yaklaşımının dezavantajlarını nasıl ortadan kaldırıyor, birer birer inceleyelim.

  • Kodlar birbirini etkilememektedir. Örneğin; ZCL_DECORATOR02 içerisindeki CHECK komutu, diğer sınıflarda yer alan kodların çalışmasını etkilememiştir.
  • ZCL_DECORATOR03’te bir değişiklik yaptığımızda, 01 ve 02’yi tekrar test etmemiz gerekmiyor.
  • 3 farklı ABAP’çı, 3 farklı sınıf üzerinde paralel çalışabilir.
  • ZCL_DECORATOR01 canlıya giderken, ZCL_DECORATOR02 geliştirme sisteminde bekleyebilir.
  • Bu Exit’e ek yapmak için tek yapmamız gereken şey ZCL_DECORATOR04 diye yeni bir sınıf açmaktır. Kodlar basit ve modüler parçacıklar halinde olduğundan, değişiklik yönetimi kolaylaşmaktadır.

Yardımcı Diğer Pattern’lar

Sınıflar arasında veri paylaşma ihtiyacı ortaya çıkarsa, Decorator’a yardımcı Property Container Design Pattern uygulanabilir. Bu Pattern; her bir User Exit için ayrı bir Interface açma ihtiyacını ortadan kaldırıp, tüm User Exit’leri (SE19 benzeri) tek bir altyapıdan yönetmeyi de mümkün kılabilir.

Decorator sınıfları arasında, birbirine benzer iş yapan sınıfların işlevleri Template Method veya Servant uygulanarak ortaklaştırılabilir.

Sıralı çağırmak gibi basit ihtiyaçların ötesinde sınıfların koordine edilmesi gerekiyorsa, Mediator kullanılabilir.

Bahsetilen bu Pattern’ların tamamı, örnek uygulamalarla birlikte Design Patterns in ABAP Objects adlı kitabımda var.

Sonuç

Bu makalede, Decorator Design Pattern’i örnek User Exit uygulaması üzerinden incelemiş olduk. Design Pattern kullanmanın avantajlarını da kısmen gördük.

İncelediğimiz bu örnek Pattern’in geliştirmelerinizde faydalı olmasını ve diğer Pattern’leri öğrenmek konusunda sizi teşvik etmesini umuyorum.

Advertisement

Posted

in

,

by

Tags:

Comments

2 responses to “Decorator Design Pattern”

  1. […] Decorator: Aynı veri üzerinde birden fazla değişiklik yapılması gerektiği durumlarda kullanılır. Bu değişikliklerin tamamını ana programa veya merkezi bir sınıfa kodlamak yerine; tak-çıkar mantığındaki mikro sınıflara kodlama prensibine dayanır. […]

  2. Özkan Avatar
    Özkan

    çok işime yaradı, elinize sağlık.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s