Refactor / redesign:

- Moved 'updateQuality' related code to another class (QualityUpdater);
- Introduced 'Category' for an Item category, which specifies how an Item should update its quality;
- Introduced 'StandardQualityDegrader' for Default Category and for 'Conjured' items which degrade twice as fast;
- Completed tests.
This commit is contained in:
Bart Guijt 2021-12-22 13:18:13 +01:00
parent 1d0671a186
commit 0ca0110ef3
7 changed files with 235 additions and 54 deletions

View File

@ -0,0 +1,27 @@
package com.gildedrose;
import java.util.function.ToIntFunction;
public class Category {
private final String name;
private ToIntFunction<Item> updateQualityFunction;
public Category(String name) {
this.name = name;
}
public Category withQualityUpdater(ToIntFunction<Item> updateQualityFunction) {
this.updateQualityFunction = updateQualityFunction;
return this;
}
public boolean isItemMyCategory(String itemName) {
return itemName != null && itemName.toLowerCase().startsWith(name.toLowerCase());
}
public void updateItem(Item item) {
item.sellIn--;
item.quality = Math.max(0, Math.min(50, updateQualityFunction.applyAsInt(item)));
}
}

View File

@ -0,0 +1,9 @@
package com.gildedrose;
public class DefaultCategory extends Category {
public DefaultCategory() {
super(null);
withQualityUpdater(new StandardQualityDegrader());
}
}

View File

@ -3,60 +3,13 @@ package com.gildedrose;
class GildedRose {
Item[] items;
private final QualityUpdater qualityUpdater = new QualityUpdater();
public GildedRose(Item[] items) {
this.items = items;
}
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;
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;
}
}
}
}
qualityUpdater.updateQuality(items);
}
}
}

View File

@ -0,0 +1,13 @@
package com.gildedrose;
public class LegendaryCategory extends Category {
public LegendaryCategory(String name) {
super(name);
}
@Override
public void updateItem(Item item) {
// Legendary Items never alter
}
}

View File

@ -0,0 +1,55 @@
package com.gildedrose;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
public class QualityUpdater {
// The 'categories' form the actual quality update specifications:
private final Category[] categories = new Category[] {
// "Aged Brie" actually increases in Quality the older it gets:
new Category("Aged Brie")
.withQualityUpdater(item -> item.quality + 1),
// "Backstage passes", like aged brie, increases in Quality as its SellIn value approaches.
// Quality increases by 2 when there are 10 days or less and by 3 when there are 5 days or less but
// Quality drops to 0 after the concert:
new Category("Backstage passes")
.withQualityUpdater(item -> {
if (item.sellIn < 0) {
return 0;
}
if (item.sellIn < 5) {
return item.quality + 3;
}
if (item.sellIn < 10) {
return item.quality + 2;
}
return item.quality + 1;
}),
// "Sulfuras", being a legendary item, never has to be sold or decreases in Quality:
new LegendaryCategory("Sulfuras"),
// "Conjured" items degrade in Quality twice as fast as normal items:
new Category("Conjured")
.withQualityUpdater(new StandardQualityDegrader(2))
};
// Maps an Item name to a specific Category,
// only to improve `findCategory()` in terms of speed:
private final Map<String, Category> categoryByItemName = new HashMap<>();
public void updateQuality(Item[] items) {
Arrays.stream(items)
.forEach(item -> categoryByItemName.computeIfAbsent(item.name, this::findCategory).updateItem(item));
}
private Category findCategory(String itemName) {
return Arrays.stream(categories)
.filter(category -> category.isItemMyCategory(itemName))
.findFirst()
.orElseGet(DefaultCategory::new);
}
}

View File

@ -0,0 +1,22 @@
package com.gildedrose;
import java.util.function.ToIntFunction;
public class StandardQualityDegrader implements ToIntFunction<Item> {
private final int speed;
public StandardQualityDegrader() {
this(1);
}
public StandardQualityDegrader(int speed) {
this.speed = speed;
}
@Override
public int applyAsInt(Item item) {
// Once the sell by date has passed, Quality degrades twice as fast:
return item.sellIn >= 0 ? item.quality - speed : item.quality - speed * 2;
}
}

View File

@ -2,16 +2,118 @@ package com.gildedrose;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertAll;
import static org.junit.jupiter.api.Assertions.assertEquals;
class GildedRoseTest {
@Test
void foo() {
Item[] items = new Item[] { new Item("foo", 0, 0) };
void testAgedBrieItems() {
Item[] items = new Item[] {
new Item("Aged Brie", 100, 10),
new Item("Aged Brie", 10, 40),
new Item("Aged Brie", 2, 49),
new Item("Aged Brie", 1, 50),
};
GildedRose app = new GildedRose(items);
app.updateQuality();
assertEquals("fixme", app.items[0].name);
assertItem(items[0], "Aged Brie", 99, 11);
assertItem(items[1], "Aged Brie", 9, 41);
assertItem(items[2], "Aged Brie", 1, 50);
assertItem(items[3], "Aged Brie", 0, 50);
}
@Test
void testSulfurasItems() {
Item[] items = new Item[] {
new Item("Sulfuras, Hand of Ragnaros", 100, 80),
new Item("Sulfuras, Hand of Ragnaros", -1, 80),
};
GildedRose app = new GildedRose(items);
app.updateQuality();
assertItem(items[0], "Sulfuras, Hand of Ragnaros", 100, 80);
assertItem(items[1], "Sulfuras, Hand of Ragnaros", -1, 80);
}
@Test
void testBackstagePassesItems() {
Item[] items = new Item[] {
new Item("Backstage passes to a TAFKAL80ETC concert", 11, 5),
new Item("Backstage passes to a TAFKAL80ETC concert", 10, 5),
new Item("Backstage passes to a TAFKAL80ETC concert", 9, 5),
new Item("Backstage passes to a TAFKAL80ETC concert", 6, 5),
new Item("Backstage passes to a TAFKAL80ETC concert", 5, 5),
new Item("Backstage passes to a TAFKAL80ETC concert", 4, 5),
new Item("Backstage passes to a TAFKAL80ETC concert", 1, 5),
new Item("Backstage passes to a TAFKAL80ETC concert", 0, 5),
};
GildedRose app = new GildedRose(items);
app.updateQuality();
assertItem(items[0], "Backstage passes to a TAFKAL80ETC concert", 10, 6);
assertItem(items[1], "Backstage passes to a TAFKAL80ETC concert", 9, 7);
assertItem(items[2], "Backstage passes to a TAFKAL80ETC concert", 8, 7);
assertItem(items[3], "Backstage passes to a TAFKAL80ETC concert", 5, 7);
assertItem(items[4], "Backstage passes to a TAFKAL80ETC concert", 4, 8);
assertItem(items[5], "Backstage passes to a TAFKAL80ETC concert", 3, 8);
assertItem(items[6], "Backstage passes to a TAFKAL80ETC concert", 0, 8);
assertItem(items[7], "Backstage passes to a TAFKAL80ETC concert", -1, 0);
}
@Test
void testStandardItems() {
Item[] items = new Item[] {
new Item("NoParticularName", 25, 30),
new Item("NoParticularName", 1, 30),
new Item("NoParticularName", 0, 30),
new Item("NoParticularName", -1, 30),
new Item("NoParticularName", 1, 1),
new Item("NoParticularName", 0, 0),
};
GildedRose app = new GildedRose(items);
app.updateQuality();
assertItem(items[0], "NoParticularName", 24, 29);
assertItem(items[1], "NoParticularName", 0, 29);
assertItem(items[2], "NoParticularName", -1, 28);
assertItem(items[3], "NoParticularName", -2, 28);
assertItem(items[4], "NoParticularName", 0, 0);
assertItem(items[5], "NoParticularName", -1, 0);
}
@Test
void testConjuredItems() {
Item[] items = new Item[] {
new Item("Conjured Mana Cake", 25, 30),
new Item("Conjured Mana Cake", 1, 30),
new Item("Conjured Mana Cake", 0, 30),
new Item("Conjured Mana Cake", -1, 30),
new Item("Conjured Mana Cake", 1, 1),
new Item("Conjured Mana Cake", 0, 0),
};
GildedRose app = new GildedRose(items);
app.updateQuality();
assertItem(items[0], "Conjured Mana Cake", 24, 28);
assertItem(items[1], "Conjured Mana Cake", 0, 28);
assertItem(items[2], "Conjured Mana Cake", -1, 26);
assertItem(items[3], "Conjured Mana Cake", -2, 26);
assertItem(items[4], "Conjured Mana Cake", 0, 0);
assertItem(items[5], "Conjured Mana Cake", -1, 0);
}
private void assertItem(Item item, String name, int sellIn, int quality) {
assertAll(
() -> assertEquals(name, item.name, "item name"),
() -> assertEquals(sellIn, item.sellIn, "item sellIn"),
() -> assertEquals(quality, item.quality, "item quality")
);
}
}