login as:
~/abapcraft.dev — code, crafted in SAP
florin@s4hana:~/abap/posts/gilded-rose $ cat README.md

Gilded Rose Kata in ABAP

A classic refactoring kata solved with Strategy + Template Method patterns — polymorphism, abstract classes, and COND #(...) in modern ABAP OOP.

The Kata

The Gilded Rose is a well-known refactoring kata originally by Terry Hughes, popularised by Emily Bache. The premise: a small inn sells items whose quality degrades over time according to rules that are deceptively simple — until you add edge cases.

The rules:

  • Every item has sell_in (days left to sell) and quality (0–50)
  • Each day, both values decrease by 1
  • Once sell_in < 0, quality degrades twice as fast
  • Aged Brie increases in quality the older it gets
  • Sulfuras is a legendary item: never sold, never degrades
  • Backstage passes increase in quality as the concert approaches (+2 with ≤10 days, +3 with ≤5 days), then drop to 0 after the show
  • Conjured items degrade at twice the normal rate

The catch: the original implementation is a single deeply-nested IF/ELSEIF block. The task is to add the Conjured item without touching the legacy ycl_item data class.

The Design

Rather than extending the IF chain, the solution applies two patterns:

Strategy — each item type gets its own class that knows how to update itself.
Factoryycl_item_shop reads the item name and hands back the right strategy object.

The orchestrator ycl_gilded_rose stays completely unaware of item-type logic:

METHOD update_quality.
  LOOP AT mt_items INTO DATA(lo_item).
    ycl_item_shop=>create_item( lo_item )->update( ).
  ENDLOOP.
ENDMETHOD.

One line of business logic. New item types require zero changes here.

The Class Hierarchy

ycl_item_root is an abstract base class that enforces the strategy contract (update ABSTRACT) and provides protected helper methods shared by all concrete types:

CLASS ycl_item_root DEFINITION PUBLIC ABSTRACT CREATE PUBLIC.
  PUBLIC SECTION.
    METHODS update ABSTRACT.
  PROTECTED SECTION.
    METHODS increase_quality.
    METHODS decrease_quality.
    METHODS decrease_sell_in.
    METHODS is_sellin_under
      IMPORTING iv_comparing_value TYPE i
      RETURNING VALUE(rv_result)   TYPE boole_d.
    DATA mo_item TYPE REF TO ycl_item.
ENDCLASS.

is_sellin_under uses xsdbool — the idiomatic way to return a boolean from a comparison in ABAP without an IF:

METHOD is_sellin_under.
  rv_result = xsdbool( mo_item->mv_sell_in < iv_comparing_value ).
ENDMETHOD.

The Factory

ycl_item_shop=>create_item uses COND #(...) as a concise type-dispatch table. Adding a new item type is a single WHEN line — existing branches are untouched:

METHOD create_item.
  ro_item = COND #(
    WHEN io_item->mv_name = mc_items-aged_brie THEN NEW ycl_aged_brie( io_item )
    WHEN io_item->mv_name = mc_items-backstage THEN NEW ycl_backstage( io_item )
    WHEN io_item->mv_name = mc_items-sulfuras  THEN NEW ycl_sulfuras( io_item )
    WHEN io_item->mv_name = mc_items-conjured  THEN NEW ycl_conjured_cake( io_item )
    ELSE NEW ycl_common_item( io_item )
  ).
ENDMETHOD.

Concrete Strategies

Common item

Decrease quality, tick sell_in, decrease quality again if past the sell-by date:

METHOD update.                   " ycl_common_item
  decrease_quality( ).
  decrease_sell_in( ).
  IF is_sellin_under( 0 ).
    decrease_quality( ).
  ENDIF.
ENDMETHOD.

Aged Brie

Same structure as common, but increase_quality instead of decrease:

METHOD update.                   " ycl_aged_brie
  increase_quality( ).
  decrease_sell_in( ).
  IF is_sellin_under( 0 ).
    increase_quality( ).
  ENDIF.
ENDMETHOD.

Backstage passes

Three quality thresholds, all expressed via is_sellin_under. After the concert the quality is wiped by subtracting itself — a nod to the kata’s no-direct-assignment constraint:

METHOD update.                   " ycl_backstage
  increase_quality( ).
  IF is_sellin_under( 11 ). increase_quality( ). ENDIF.
  IF is_sellin_under( 6  ). increase_quality( ). ENDIF.
  decrease_sell_in( ).
  IF is_sellin_under( 0  ). abolish_quality( ).  ENDIF.
ENDMETHOD.

METHOD abolish_quality.
  mo_item->mv_quality = mo_item->mv_quality - mo_item->mv_quality.
ENDMETHOD.

Sulfuras

The legendary item implementation is, appropriately, empty:

METHOD update.                   " ycl_sulfuras
ENDMETHOD.

Conjured — Template Method in action

This is the most elegant part of the design. ycl_conjured_cake inherits the same update body as ycl_common_item, but overrides decrease_quality to subtract 2 instead of 1. The kata’s double-degradation rule is implemented by changing one protected method, not the orchestration:

METHOD update.                   " inherited — same as ycl_common_item
  decrease_quality( ).
  decrease_sell_in( ).
  IF is_sellin_under( 0 ).
    decrease_quality( ).
  ENDIF.
ENDMETHOD.

METHOD decrease_quality.         " REDEFINITION — doubles the degradation
  mo_item->mv_quality =
    COND #( WHEN mo_item->mv_quality GT 2
            THEN mo_item->mv_quality - 2
            ELSE 0 ).
ENDMETHOD.

This is the Template Method pattern embedded inside the Strategy hierarchy: the base class defines when quality changes happen, subclasses control how much.

Tests

Every concrete class ships with a testclasses.abap section. The test for the factory lives in ycl_gilded_rose.clas.testclasses.abap and drives the whole system end-to-end:

METHOD test_aged_brie_increases.
  DATA(lo_item) = NEW ycl_item( iv_name = 'Aged Brie' iv_sell_in = 5 iv_quality = 10 ).
  DATA(lo_shop) = NEW ycl_gilded_rose( it_items = VALUE #( ( lo_item ) ) ).
  lo_shop->update_quality( ).
  cl_abap_unit_assert=>assert_equals( exp = 11 act = lo_item->mv_quality ).
ENDMETHOD.

Run all tests with ABAP Unit in SE24 or via abapunit on the command line.

Watch on YouTube

The full kata walkthrough — from the legacy IF/ELSEIF mess to the finished Strategy hierarchy — is recorded on YouTube:

▶ Gilded Rose Kata in ABAP — full walkthrough

Clone into Your System

1. abapGit → New Online Repository
2. URL: https://github.com/CiozZ/gilded_rose
3. Package: $GILDED_ROSE (or any local package)
4. Pull → Activate all
5. Run ABAP Unit on YCL_GILDED_ROSE