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) andquality(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.
Factory — ycl_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