Refactoring GildedRose app code. Rebuild main logic with factory pattern. Add items specific classes. Extract constants. Optimize logic. Add unit tests. Adapt kata configs to work with windows env and java 21.

This commit is contained in:
stanislaw.rusnak 2025-06-03 19:32:21 +02:00
parent d3057d9fb1
commit 92d3c65925
10 changed files with 272 additions and 56 deletions

View File

@ -0,0 +1,13 @@
package com.gildedrose;
class AgedBrieItem extends UpdatableItem {
public AgedBrieItem(Item item) {
super(item);
}
@Override
public void update() {
incrementQuality(STANDARD_VALUE_CHANGE);
decrementSellIn();
}
}

View File

@ -0,0 +1,32 @@
package com.gildedrose;
class BackstagePassItem extends UpdatableItem {
private static final int FIRST_SELL_IN_THRESHOLD = 10;
private static final int SECOND_SELL_IN_THRESHOLD = 5;
public BackstagePassItem(Item item) {
super(item);
}
@Override
public void update() {
decrementSellIn();
updateQuality();
}
private void updateQuality() {
if (isOutdated()) {
setZeroQuality();
} else {
incrementQuality(getIncrementValue());
}
}
private int getIncrementValue() {
int increment = STANDARD_VALUE_CHANGE;
if (item.sellIn <= FIRST_SELL_IN_THRESHOLD) increment++;
if (item.sellIn <= SECOND_SELL_IN_THRESHOLD) increment++;
return increment;
}
}

View File

@ -0,0 +1,14 @@
package com.gildedrose;
class ConjuredItem extends UpdatableItem {
public ConjuredItem(Item item) {
super(item);
}
@Override
public void update() {
decrementQuality(DOUBLE_VALUE_CHANGE);
decrementSellIn();
}
}

View File

@ -1,6 +1,13 @@
package com.gildedrose;
import java.util.Arrays;
class GildedRose {
private static final String BACKSTAGE_PASS_NAME = "Backstage passes to a TAFKAL80ETC concert";
private static final String SULFURAS_NAME = "Sulfuras, Hand of Ragnaros";
private static final String AGED_BRIE_NAME = "Aged Brie";
private static final String CONJURED_NAME = "Conjured";
Item[] items;
public GildedRose(Item[] items) {
@ -8,55 +15,18 @@ class GildedRose {
}
public void updateQuality() {
for (int i = 0; i < items.length; i++) {
if (!items[i].name.equals("Aged Brie")
&& !items[i].name.equals("Backstage passes to a TAFKAL80ETC concert")) {
if (items[i].quality > 0) {
if (!items[i].name.equals("Sulfuras, Hand of Ragnaros")) {
items[i].quality = items[i].quality - 1;
}
}
} else {
if (items[i].quality < 50) {
items[i].quality = items[i].quality + 1;
Arrays.stream(items)
.map(this::mapToUpdatableItem)
.forEach(UpdatableItem::update);
}
if (items[i].name.equals("Backstage passes to a TAFKAL80ETC concert")) {
if (items[i].sellIn < 11) {
if (items[i].quality < 50) {
items[i].quality = items[i].quality + 1;
}
}
if (items[i].sellIn < 6) {
if (items[i].quality < 50) {
items[i].quality = items[i].quality + 1;
}
}
}
}
}
if (!items[i].name.equals("Sulfuras, Hand of Ragnaros")) {
items[i].sellIn = items[i].sellIn - 1;
}
if (items[i].sellIn < 0) {
if (!items[i].name.equals("Aged Brie")) {
if (!items[i].name.equals("Backstage passes to a TAFKAL80ETC concert")) {
if (items[i].quality > 0) {
if (!items[i].name.equals("Sulfuras, Hand of Ragnaros")) {
items[i].quality = items[i].quality - 1;
}
}
} else {
items[i].quality = items[i].quality - items[i].quality;
}
} else {
if (items[i].quality < 50) {
items[i].quality = items[i].quality + 1;
}
}
}
}
private UpdatableItem mapToUpdatableItem(Item item) {
return switch (item.name) {
case AGED_BRIE_NAME -> new AgedBrieItem(item);
case BACKSTAGE_PASS_NAME -> new BackstagePassItem(item);
case SULFURAS_NAME -> new SulfurasItem(item);
case CONJURED_NAME -> new ConjuredItem(item);
default -> new RegularItem(item);
};
}
}

View File

@ -0,0 +1,19 @@
package com.gildedrose;
class RegularItem extends UpdatableItem {
public RegularItem(Item item) {
super(item);
}
@Override
public void update() {
decrementSellIn();
updateQuality();
}
private void updateQuality() {
int decrementAmount = isOutdated() ? DOUBLE_VALUE_CHANGE : STANDARD_VALUE_CHANGE;
decrementQuality(decrementAmount);
}
}

View File

@ -0,0 +1,14 @@
package com.gildedrose;
class SulfurasItem extends UpdatableItem {
public static final int SULFURAS_CONSTANT_QUALITY = 80;
public SulfurasItem(Item item) {
super(item);
item.quality = SULFURAS_CONSTANT_QUALITY;
}
@Override
public void update() {}
}

View File

@ -0,0 +1,41 @@
package com.gildedrose;
import static java.lang.Math.max;
import static java.lang.Math.min;
abstract class UpdatableItem {
protected static final int DOUBLE_VALUE_CHANGE = 2;
protected static final int STANDARD_VALUE_CHANGE = 1;
protected static final int QUALITY_MAX = 50;
protected static final int QUALITY_MIN = 0;
protected static final int ZERO_SELL_IN = 0;
protected Item item;
protected UpdatableItem(Item item) {
this.item = item;
}
public abstract void update();
protected void incrementQuality(int incrementAmount) {
item.quality = min(QUALITY_MAX, item.quality + incrementAmount);
}
protected void setZeroQuality() {
item.quality = QUALITY_MIN;
}
protected void decrementQuality(int decrementAmount) {
item.quality = max(QUALITY_MIN, item.quality - decrementAmount);
}
protected void decrementSellIn() {
item.sellIn--;
}
protected boolean isOutdated() {
return item.sellIn < ZERO_SELL_IN;
}
}

View File

@ -7,11 +7,124 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
class GildedRoseTest {
@Test
void foo() {
Item[] items = new Item[] { new Item("foo", 0, 0) };
void shouldDefaultDecreaseQualityBeforeSellIn() {
Item[] items = new Item[]{
new Item("testProductName", 9, 30)
};
GildedRose app = new GildedRose(items);
app.updateQuality();
assertEquals("fixme", app.items[0].name);
assertEquals("testProductName", app.items[0].name);
assertEquals(29, app.items[0].quality);
}
@Test
void shouldDoubleDecreaseQualityAfterSellIn() {
Item[] items = new Item[]{
new Item("testProductName", -2, 30)
};
GildedRose app = new GildedRose(items);
app.updateQuality();
assertEquals("testProductName", app.items[0].name);
assertEquals(28, app.items[0].quality);
}
@Test
void shouldNotDecreaseQualityBelowZero() {
Item[] items = new Item[]{
new Item("testProductName", 9, 0)
};
GildedRose app = new GildedRose(items);
app.updateQuality();
assertEquals("testProductName", app.items[0].name);
assertEquals(0, app.items[0].quality);
}
@Test
void shouldIncreaseQualityOfAgedBrie() {
Item[] items = new Item[]{
new Item("Aged Brie", 9, 30)
};
GildedRose app = new GildedRose(items);
app.updateQuality();
assertEquals("Aged Brie", app.items[0].name);
assertEquals(31, app.items[0].quality);
}
@Test
void shouldNotIncreaseQualityAboveFifty() {
Item[] items = new Item[]{
new Item("Aged Brie", 9, 50)
};
GildedRose app = new GildedRose(items);
app.updateQuality();
assertEquals("Aged Brie", app.items[0].name);
assertEquals(50, app.items[0].quality);
}
@Test
void shouldNotChangeSulfurasQuality() {
Item[] items = new Item[]{
new Item("Sulfuras, Hand of Ragnaros", 9, 80)
};
GildedRose app = new GildedRose(items);
app.updateQuality();
assertEquals("Sulfuras, Hand of Ragnaros", app.items[0].name);
assertEquals(80, app.items[0].quality);
}
@Test
void shouldIncreaseQualityBy2Below10DaysForSpecialProducts() {
Item[] items = new Item[]{
new Item("Backstage passes to a TAFKAL80ETC concert", 9, 32)
};
GildedRose app = new GildedRose(items);
app.updateQuality();
assertEquals("Backstage passes to a TAFKAL80ETC concert", app.items[0].name);
assertEquals(34, app.items[0].quality);
}
@Test
void shouldIncreaseQualityBy3AfterBelow5DaysForSpecialProducts() {
Item[] items = new Item[]{
new Item("Backstage passes to a TAFKAL80ETC concert", 4, 32)
};
GildedRose app = new GildedRose(items);
app.updateQuality();
assertEquals("Backstage passes to a TAFKAL80ETC concert", app.items[0].name);
assertEquals(35, app.items[0].quality);
}
@Test
void shouldSetZeroQualityForSpecialProductsAfterSellIn() {
Item[] items = new Item[]{
new Item("Backstage passes to a TAFKAL80ETC concert", -2, 32)
};
GildedRose app = new GildedRose(items);
app.updateQuality();
assertEquals("Backstage passes to a TAFKAL80ETC concert", app.items[0].name);
assertEquals(0, app.items[0].quality);
}
@Test
void shouldDoubleDecreaseQualityForConjuredItems() {
Item[] items = new Item[]{
new Item("Conjured", 9, 30)
};
GildedRose app = new GildedRose(items);
app.updateQuality();
assertEquals("Conjured", app.items[0].name);
assertEquals(28, app.items[0].quality);
}
@Test
void shouldNotExceedQualityLimitForBackstageItem() {
Item[] items = new Item[]{
new Item("Backstage passes to a TAFKAL80ETC concert", 4, 49)
};
GildedRose app = new GildedRose(items);
app.updateQuality();
assertEquals("Backstage passes to a TAFKAL80ETC concert", app.items[0].name);
assertEquals(50, app.items[0].quality);
}
}

View File

@ -3,5 +3,5 @@
if [ ! -d "venv" ]; then
python -m venv venv
fi
venv/bin/pip install texttest
venv/bin/texttest -d . -con "$@"
venv/Scripts/pip.exe install texttest
venv/Scripts/texttest.exe -d . -con "$@"

View File

@ -18,8 +18,8 @@ diff_program:meld
#executable:${TEXTTEST_HOME}/zig/zig-out/bin/zig
# Settings for the Java version using Gradle wrapped in a python script
#executable:${TEXTTEST_HOME}/Java/texttest_rig.py
#interpreter:python
executable:${TEXTTEST_HOME}/Java/texttest_rig.py
interpreter:python
# Settings for the Java version using the classpath
#executable:com.gildedrose.TexttestFixture