After the in-character refactoring of the Gilded Rose kata ( GildedWoes ), a "normie" refactoring would likely involve making a class for each product. However, a rules-based refactoring seems cleaner.
Rules-based has two strong advantages over other implementations: 1) since the requirements are also rules-based, verifying them is straightforward, and 2) the previous implementation's variance with requirements is made obvious.
In the typical products-are-objects implementation, Sulfuras is the "normal" object, since the class does nothing. Adding in a rules-based refactoring makes the normal item the normal object -- i.e. something similar to the following:
class Rules(object): default_increment = -1 def ruleSellInDecrement(item): item.sell_in -= 1 def ruleSetIncrement(item): item.incr = item.default_increment def ruleSellInDoubles(item): if item.sell_in < 0: item.incr = 2 * item.incr def ruleHandleLowOutOfRangeAsPerPreviousImplementation(item): if item.quality < 0: item.incr = 0 def ruleHandleHighOutOfRangeAsPerPreviousImplementation(item): if item.quality > 50 and item.incr > 0: item.incr = 0 def ruleNeverGrowNegative(item): if item.incr < 0 and item.quality + item.incr < 0: item.quality = 0 item.incr = 0 def ruleNeverGrowOver50(item): if item.incr > 0 and item.quality + item.incr > 50: item.quality = 50 item.incr = 0 def ruleAddIncrement(item): item.quality += item.incr def ruleBackstageZeroing(item): pass rules = [ x for x in locals() if x.startswith('rule') ] class ItemNormal(Item, Rules): pass class ItemConjured(Item, Rules): default_increment = 2 * ItemNormal.default_increment class ItemBrie(Item, Rules): default_increment = 1 def ruleHandleLowOutOfRangeAsPerPreviousImplementation(item): pass class ItemBackstage(Item, Rules): def ruleSetIncrement(item): item.incr = sum([ item.sell_in >= 0, 0 <= item.sell_in < 5, 0 <= item.sell_in < 10 ]) def ruleHandleLowOutOfRangeAsPerPreviousImplementation(item): pass def ruleBackstageZeroing(item): if item.sell_in < 0: item.quality = 0 class ItemSulfuras(Item, Rules): default_increment = 0 def ruleSellInDecrement(item): pass class GildedRose(object): def __init__(self, items): objmap = { "Sulfuras, Hand of Ragnaros": ItemSulfuras, "Conjured code": ItemConjured, "Backstage passes to a TAFKAL80ETC concert": ItemBackstage, "Aged Brie": ItemBrie } for item in items: item.__class__ = objmap[item.name] if item.name in objmap else ItemNormal self.items = items def update(self): for item in self.items: for rule in item.rules: getattr(item, rule)() return self.items