Genel

ABAP OOP ile Tasarım Desenleri: ABAP OOP ve Strategy Pattern Gerçek Dünya Uygulamaları

23 Mart 2026 · 11 dk okuma · aixsap
ABAP OOP ile Tasarım Desenleri: ABAP OOP ve Strategy Pattern Gerçek Dünya Uygulamaları

Yıllar önce bir SAP projesinde, üretim planlama modülü için karmaşık bir fiyatlandırma motoru geliştirmem gerekiyordu. Müşterinin talebi netti: onlarca farklı fiyatlandırma kuralı, farklı ülkeler için farklı vergi hesaplamaları ve her çeyrek değişebilecek iş mantığı. Procedural ABAP ile başladım—sonuç? Yüzlerce satır CASE deyimi, her yeni kural geldiğinde büyüyen bir bakım cehennemi. O günden bu yana OOP tasarım desenleri benim için bir kurtarıcı oldu.

Bu makalede, ABAP’ta nesne yönelimli programlamanın gerçek gücünü ortaya koyan iki temel tasarım desenini—Factory Method ve Strategy Pattern—elle tutulur örneklerle inceliyoruz. SAP geliştirme dünyasında bu desenleri ne zaman, neden ve nasıl kullanmanız gerektiğini, hazır ABAP kod örnekleriyle birlikte ele alacağız.

“Tasarım desenleri, daha önce çözülmüş problemlerin kanıtlanmış çözümleridir. Tekerleği yeniden icat etmek yerine, atölye pratiğini mirasınıza dahil edin.”

Neden ABAP’ta OOP Tasarım Desenleri Kullanmalısınız?

ABAP geliştiricilerinin önemli bir kısmı hâlâ procedural yaklaşımla kod yazıyor. Bu anlaşılabilir bir durum—SAP’ın kökleri FM (Function Module) ve Report programlara dayanıyor. Ancak S/4HANA geçişleriyle birlikte, iş mantığının karmaşıklığı ve bakım yükü artık OOP’u zorunlu hale getiriyor.

Tasarım desenlerinin size kazandırdıkları:

  • Esneklik: Mevcut kodu bozmadan yeni davranışlar ekleyebilirsiniz
  • Test Edilebilirlik: Bağımlılıkları soyutladığınızda unit test yazmak kolaylaşır
  • Okunabilirlik: Ekip üyeleri niyetinizi kod yapısından anlayabilir
  • SAP BTP Uyumu: Clean Core prensipleriyle doğal uyum sağlar

Eğer temiz ve sürdürülebilir ABAP kod yazımı konusunu daha kapsamlı ele almak istiyorsanız, ABAP Clean Code: Okunabilir ve Sürdürülebilir SAP Kodu Yazmanın 10 Altın Kuralı makalemize göz atmanızı öneririm—bu makale onun pratik bir devamı niteliğinde.

Factory Method Pattern: Nesne Yaratımını Soyutlayın

Problem: Doğrudan NEW Operatörü Kullanmanın Riskleri

Şöyle bir senaryo düşünün: Farklı belge türleri (fatura, sipariş, iade) için farklı işlemci sınıfları yazıyorsunuz. Naif yaklaşım şu olurdu:


" KÖTÜ YAKLAŞIM - Bağımlılıkları doğrudan bağlamak
CASE lv_doc_type.
  WHEN 'INVOICE'.
    lo_processor = NEW zcl_invoice_processor( ).
  WHEN 'ORDER'.
    lo_processor = NEW zcl_order_processor( ).
  WHEN 'RETURN'.
    lo_processor = NEW zcl_return_processor( ).
ENDCASE.

Sorun şu: Yeni bir belge türü her geldiğinde bu CASE deyimini aramak ve değiştirmek zorundasınız. Açık/Kapalı prensibini (Open/Closed Principle) ihlal ediyorsunuz.

Factory Method ile Çözüm

Önce bir arayüz tanımlayalım:


" Arayüz tanımı
INTERFACE zif_doc_processor.
  METHODS:
    process
      IMPORTING
        is_document TYPE zs_document
      RETURNING
        VALUE(rv_result) TYPE string,
    validate
      IMPORTING
        is_document TYPE zs_document
      RETURNING
        VALUE(rv_is_valid) TYPE abap_bool.
ENDINTERFACE.

Her belge türü için concrete implementation:


" Fatura işlemcisi
CLASS zcl_invoice_processor DEFINITION PUBLIC.
  PUBLIC SECTION.
    INTERFACES zif_doc_processor.
ENDCLASS.

CLASS zcl_invoice_processor IMPLEMENTATION.
  METHOD zif_doc_processor~process.
    " Fatura işleme mantığı
    rv_result = |Invoice { is_document-doc_number } processed|.
  ENDMETHOD.

  METHOD zif_doc_processor~validate.
    " Fatura doğrulama mantığı
    rv_is_valid = xsdbool( is_document-amount > 0 ).
  ENDMETHOD.
ENDCLASS.

Ve şimdi Factory sınıfı:


" Factory sınıfı
CLASS zcl_doc_processor_factory DEFINITION PUBLIC FINAL.
  PUBLIC SECTION.
    CLASS-METHODS:
      create
        IMPORTING
          iv_doc_type TYPE string
        RETURNING
          VALUE(ro_processor) TYPE REF TO zif_doc_processor
        RAISING
          zcx_unknown_doc_type.

  PRIVATE SECTION.
    " Processor registry - yeni tipler buraya eklenir
    CLASS-DATA:
      gt_registry TYPE TABLE OF REF TO zif_doc_processor.
ENDCLASS.

CLASS zcl_doc_processor_factory IMPLEMENTATION.
  METHOD create.
    CASE iv_doc_type.
      WHEN 'INVOICE'.
        ro_processor = NEW zcl_invoice_processor( ).
      WHEN 'ORDER'.
        ro_processor = NEW zcl_order_processor( ).
      WHEN 'RETURN'.
        ro_processor = NEW zcl_return_processor( ).
      WHEN OTHERS.
        RAISE EXCEPTION TYPE zcx_unknown_doc_type
          EXPORTING
            mv_doc_type = iv_doc_type.
    ENDCASE.
  ENDMETHOD.
ENDCLASS.

Kullanımı artık son derece temiz:


" Kullanım - İstemci kodu
TRY.
    DATA(lo_processor) = zcl_doc_processor_factory=>create(
      iv_doc_type = ls_header-doc_type
    ).

    IF lo_processor->validate( is_document = ls_document ).
      DATA(lv_result) = lo_processor->process( is_document = ls_document ).
      WRITE: / lv_result.
    ENDIF.

  CATCH zcx_unknown_doc_type INTO DATA(lx_error).
    " Hata yönetimi - bilinmeyen belge türü
    MESSAGE lx_error->get_text( ) TYPE 'E'.
ENDTRY.

Artık yeni bir belge türü eklemeniz gerektiğinde yalnızca yeni bir sınıf yazıp factory’ye bir satır ekliyorsunuz. İstemci kodu hiç değişmiyor.

Strategy Pattern: Değişen Davranışı Kapsülleyin

Problem: İş Mantığı Değişiyor, Kod Değişmemeli

SAP projelerinde sık karşılaştığım bir senaryo: Vergi hesaplama mantığı ülkeye, müşteri tipine veya ürün kategorisine göre farklılık gösteriyor. Her kombinasyon için IF/CASE deyimleri yazmak hem tehlikeli hem de bakımı imkânsız hale getiriyor.

Strategy Pattern, bir algoritma ailesini tanımlamanıza, her birini ayrı bir sınıfa koymanıza ve birbirinin yerine kullanılabilir hale getirmenize olanak tanır.

Gerçek Dünya: Fiyatlandırma Stratejileri


" Strategy arayüzü
INTERFACE zif_pricing_strategy.
  METHODS:
    calculate_price
      IMPORTING
        iv_base_price  TYPE p DECIMALS 2
        iv_quantity    TYPE i
        is_customer    TYPE zs_customer
      RETURNING
        VALUE(rv_final_price) TYPE p DECIMALS 2.

    get_strategy_name
      RETURNING
        VALUE(rv_name) TYPE string.
ENDINTERFACE.

Concrete stratejiler:


" Standart fiyatlandırma
CLASS zcl_standard_pricing DEFINITION PUBLIC.
  PUBLIC SECTION.
    INTERFACES zif_pricing_strategy.
ENDCLASS.

CLASS zcl_standard_pricing IMPLEMENTATION.
  METHOD zif_pricing_strategy~calculate_price.
    rv_final_price = iv_base_price * iv_quantity.
  ENDMETHOD.

  METHOD zif_pricing_strategy~get_strategy_name.
    rv_name = 'Standard Pricing'.
  ENDMETHOD.
ENDCLASS.

" VIP müşteri indirimli fiyatlandırma
CLASS zcl_vip_pricing DEFINITION PUBLIC.
  PUBLIC SECTION.
    INTERFACES zif_pricing_strategy.
  PRIVATE SECTION.
    CONSTANTS: c_discount_rate TYPE p DECIMALS 2 VALUE '0.15'. " %15 indirim
ENDCLASS.

CLASS zcl_vip_pricing IMPLEMENTATION.
  METHOD zif_pricing_strategy~calculate_price.
    DATA(lv_gross) = iv_base_price * iv_quantity.
    rv_final_price = lv_gross * ( 1 - c_discount_rate ).
  ENDMETHOD.

  METHOD zif_pricing_strategy~get_strategy_name.
    rv_name = 'VIP Customer Pricing (15% Discount)'.
  ENDMETHOD.
ENDCLASS.

" Toplu alım fiyatlandırması
CLASS zcl_bulk_pricing DEFINITION PUBLIC.
  PUBLIC SECTION.
    INTERFACES zif_pricing_strategy.
  PRIVATE SECTION.
    CONSTANTS:
      c_bulk_threshold  TYPE i VALUE 100,
      c_bulk_discount   TYPE p DECIMALS 2 VALUE '0.20'. " %20 indirim
ENDCLASS.

CLASS zcl_bulk_pricing IMPLEMENTATION.
  METHOD zif_pricing_strategy~calculate_price.
    DATA(lv_gross) = iv_base_price * iv_quantity.
    IF iv_quantity >= c_bulk_threshold.
      rv_final_price = lv_gross * ( 1 - c_bulk_discount ).
    ELSE.
      rv_final_price = lv_gross.
    ENDIF.
  ENDMETHOD.

  METHOD zif_pricing_strategy~get_strategy_name.
    rv_name = |Bulk Pricing (>{ c_bulk_threshold } units: 20% off)|.
  ENDMETHOD.
ENDCLASS.

Context sınıfı—stratejiyi kullanan motor:


" Context sınıfı
CLASS zcl_price_calculator DEFINITION PUBLIC.
  PUBLIC SECTION.
    METHODS:
      constructor
        IMPORTING
          io_strategy TYPE REF TO zif_pricing_strategy,

      set_strategy
        IMPORTING
          io_strategy TYPE REF TO zif_pricing_strategy,

      calculate
        IMPORTING
          iv_base_price  TYPE p DECIMALS 2
          iv_quantity    TYPE i
          is_customer    TYPE zs_customer
        RETURNING
          VALUE(rv_price) TYPE p DECIMALS 2.

  PRIVATE SECTION.
    DATA: mo_strategy TYPE REF TO zif_pricing_strategy.
ENDCLASS.

CLASS zcl_price_calculator IMPLEMENTATION.
  METHOD constructor.
    mo_strategy = io_strategy.
  ENDMETHOD.

  METHOD set_strategy.
    " Runtime'da strateji değiştirilebilir!
    mo_strategy = io_strategy.
  ENDMETHOD.

  METHOD calculate.
    rv_price = mo_strategy->calculate_price(
      iv_base_price = iv_base_price
      iv_quantity   = iv_quantity
      is_customer   = is_customer
    ).
  ENDMETHOD.
ENDCLASS.

Kullanımı—runtime’da strateji seçimi:


" Stratejiyi müşteri tipine göre belirle
DATA(lo_strategy) = COND #(
  WHEN ls_customer-type = 'VIP'  THEN NEW zcl_vip_pricing( )
  WHEN ls_order-quantity >= 100  THEN NEW zcl_bulk_pricing( )
  ELSE                                NEW zcl_standard_pricing( )
).

" Calculator oluştur
DATA(lo_calculator) = NEW zcl_price_calculator(
  io_strategy = lo_strategy
).

" Fiyatı hesapla
DATA(lv_price) = lo_calculator->calculate(
  iv_base_price = ls_product-price
  iv_quantity   = ls_order-quantity
  is_customer   = ls_customer
).

WRITE: / |Final Price: { lv_price CURRENCY 'TRY' }|.

İki Deseni Birlikte Kullanmak: Gerçek Dünya Senaryosu

En güçlü kullanım, bu iki deseni birleştirmektir. Factory, doğru stratejiyi yaratır; Strategy, davranışı kapsüller.


" Pricing Strategy Factory
CLASS zcl_pricing_factory DEFINITION PUBLIC FINAL.
  PUBLIC SECTION.
    CLASS-METHODS:
      create_strategy
        IMPORTING
          is_customer TYPE zs_customer
          iv_quantity TYPE i
        RETURNING
          VALUE(ro_strategy) TYPE REF TO zif_pricing_strategy.
ENDCLASS.

CLASS zcl_pricing_factory IMPLEMENTATION.
  METHOD create_strategy.
    " İş kurallarına göre otomatik strateji seçimi
    IF is_customer-type = 'VIP' AND is_customer-years_active >= 5.
      " Sadık VIP müşteriler en iyi fiyatı alır
      ro_strategy = NEW zcl_vip_pricing( ).
    ELSEIF iv_quantity >= 100.
      ro_strategy = NEW zcl_bulk_pricing( ).
    ELSE.
      ro_strategy = NEW zcl_standard_pricing( ).
    ENDIF.
  ENDMETHOD.
ENDCLASS.

" Artık çağıran kod son derece sade:
DATA(lo_strategy) = zcl_pricing_factory=>create_strategy(
  is_customer = ls_customer
  iv_quantity = ls_order-quantity
).

DATA(lo_calc) = NEW zcl_price_calculator( io_strategy = lo_strategy ).
DATA(lv_final) = lo_calc->calculate(
  iv_base_price = ls_product-price
  iv_quantity   = ls_order-quantity
  is_customer   = ls_customer
).

Unit Test Yazımı: Tasarım Desenlerinin Test Edilebilirliği

Bu yaklaşımın en büyük faydalarından biri test edilebilirliktir. Her strateji bağımsız olarak test edilebilir:


" ABAP Unit Test
CLASS zcl_pricing_test DEFINITION FOR TESTING
  RISK LEVEL HARMLESS
  DURATION SHORT.

  PRIVATE SECTION.
    METHODS:
      test_vip_discount     FOR TESTING,
      test_bulk_threshold   FOR TESTING,
      test_standard_pricing FOR TESTING.
ENDCLASS.

CLASS zcl_pricing_test IMPLEMENTATION.
  METHOD test_vip_discount.
    DATA(lo_strategy) = NEW zcl_vip_pricing( ).
    DATA(ls_customer) = VALUE zs_customer( type = 'VIP' ).

    DATA(lv_price) = lo_strategy->calculate_price(
      iv_base_price = '100'
      iv_quantity   = 1
      is_customer   = ls_customer
    ).

    " VIP indirim sonrası 85 olmalı (%15 indirim)
    cl_abap_unit_assert=>assert_equals(
      act = lv_price
      exp = '85'
      msg = 'VIP discount calculation failed'
    ).
  ENDMETHOD.

  METHOD test_bulk_threshold.
    DATA(lo_strategy) = NEW zcl_bulk_pricing( ).
    DATA(ls_customer) = VALUE zs_customer( ).

    " 100 adet - bulk threshold eşiğinde
    DATA(lv_price) = lo_strategy->calculate_price(
      iv_base_price = '10'
      iv_quantity   = 100
      is_customer   = ls_customer
    ).

    " 1000 * 0.80 = 800 olmalı
    cl_abap_unit_assert=>assert_equals(
      act = lv_price
      exp = '800'
      msg = 'Bulk pricing threshold test failed'
    ).
  ENDMETHOD.

  METHOD test_standard_pricing.
    DATA(lo_strategy) = NEW zcl_standard_pricing( ).
    DATA(ls_customer) = VALUE zs_customer( ).

    DATA(lv_price) = lo_strategy->calculate_price(
      iv_base_price = '50'
      iv_quantity   = 3
      is_customer   = ls_customer
    ).

    cl_abap_unit_assert=>assert_equals(
      act = lv_price
      exp = '150'
      msg = 'Standard pricing calculation failed'
    ).
  ENDMETHOD.
ENDCLASS.

S/4HANA Clean Core Perspektifinden Değerlendirme

SAP’ın Clean Core stratejisi, özelleştirmelerin SAP standart koduna dokunmamasını öngörüyor. Bu tasarım desenleri bu prensibi doğal olarak destekliyor:

  • BAdI Entegrasyonu: Factory pattern, BAdI implementasyonlarını seçmek için ideal
  • SAP BTP Uyumu: Soyutlanmış iş mantığını side-by-side extensibility ile buluta taşımak kolaylaşır
  • Upgrade Güvenliği: Standart SAP nesnelerine dokunmadığınız için yükseltmeler risksizdir

Yaygın Hatalar ve Kaçınma Yolları

Bu desenleri uygularken en sık karşılaştığım hatalar:

1. Over-engineering: Her şey için factory yazmak gerekmez. Eğer yalnızca bir implementasyon varsa ve değişmesi beklenmiyorsa, basit tutun.

2. Anemic Strategy: Strategy sınıfı yalnızca bir getter/setter içeriyorsa, muhtemelen yanlış deseni uygulamışsınızdır.

3. Gereksiz Singleton: Factory sınıflarınızı Singleton yapmaktan kaçının—state taşıyan bir factory test etmek son derece güçtür.

4. Exception Yönetimini Atlamamak: Factory her zaman bilinmeyen tip için anlamlı bir exception fırlatmalı.

Sonuç: Mimarın Perspektifi

ABAP’ta Factory Method ve Strategy Pattern, karmaşık SAP projelerinde iş mantığını yönetilebilir hale getirmenin en etkili yollarından ikisi. Yıllar içinde edindiğim deneyimle şunu söyleyebilirim: Bu desenleri öğrenmek bir haftanızı alır, ama getirisi yıllarca sürer.

Başlangıç için şu adımları öneririm:

  1. Mevcut projenizdeki büyük CASE deyimlerini belirleyin
  2. Bunları arayüz arkasına alın
  3. Her dal için concrete sınıf yazın
  4. Factory ile seçimi merkezileştirin
  5. ABAP Unit testlerinizi yazın

Bu yaklaşımı bir kez içselleştirdiğinizde, diğer tasarım desenlerini—Observer, Decorator, Command—de ABAP’a adapte etmek çok daha kolay gelecek. Bir sonraki makalede bu desenleri de ele alacağım.


Bu makaledeki kod örnekleri SAP NetWeaver 7.50 ve üzeri ile S/4HANA ortamlarında test edilmiştir. Production kullanımı öncesinde kendi geliştirme sisteminizde doğrulamanızı öneririm.

Sizin Deneyimleriniz?

SAP projelerinizde OOP tasarım desenlerini kullandınız mı? Hangi deseni en çok faydalı buldunuz? Yorumlarınızı bekliyorum—özellikle gerçek proje deneyimlerinizi duymak isterim. Eğer bu yaklaşımı denemek isterseniz ve takıldığınız bir nokta olursa, soru sormaktan çekinmeyin.

← ABAP Clean Code: Okunabilir ve… SAP ABAP'ta Exception Handling: Temiz,… →