[] Gilded Rose Refactoring Kata , including "Conjured" case and tests for all the cases

This commit is contained in:
Georgi Kazakov 2025-05-14 11:20:54 +03:00
parent d3057d9fb1
commit 08e13dcf43
13 changed files with 412 additions and 77 deletions

View File

@ -10,10 +10,12 @@
<version>0.0.1-SNAPSHOT</version>
<properties>
<java.version>1.8</java.version>
<java.version>1.9</java.version>
<junit.jupiter.version>5.8.2</junit.jupiter.version>
<maven.maven-compiler-plugin.version>3.1</maven.maven-compiler-plugin.version>
<maven.maven-surefire-plugin.version>3.0.0-M4</maven.maven-surefire-plugin.version>
<lombok.version>1.18.38</lombok.version>
<slf4j.version>2.0.17</slf4j.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
@ -24,6 +26,22 @@
<version>${junit.jupiter.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${slf4j.version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>${slf4j.version}</version>
</dependency>
</dependencies>
<build>

View File

@ -1,61 +1,61 @@
package com.gildedrose;
class GildedRose {
Item[] items;
import com.gildedrose.enums.ItemName;
import com.gildedrose.service.AgedBrieQualityUpdater;
import com.gildedrose.service.BackstagePassesQualityUpdater;
import com.gildedrose.service.ConjuredQualityUpdater;
import com.gildedrose.service.GeneralQualityUpdater;
import com.gildedrose.service.QualityUpdater;
import lombok.Data;
public GildedRose(Item[] items) {
this.items = items;
}
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import static com.gildedrose.enums.ItemName.AGED_BRIE;
import static com.gildedrose.enums.ItemName.BACKSTAGE_PASSES;
import static com.gildedrose.enums.ItemName.CONJURED;
import static com.gildedrose.enums.ItemName.GENERAL;
@Data
public class GildedRose {
private static final Map<ItemName, QualityUpdater> qualityUpdaters =
Map.of(AGED_BRIE, new AgedBrieQualityUpdater(),
BACKSTAGE_PASSES, new BackstagePassesQualityUpdater(),
CONJURED, new ConjuredQualityUpdater(),
GENERAL, new GeneralQualityUpdater());
private final List<Item> 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;
Set<ItemName> itemNames = items.stream().map(item -> ItemName.resolve(item.getName())).collect(Collectors.toSet());
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;
}
}
for (ItemName itemName : itemNames) {
QualityUpdater qualityUpdater = null;
if (items[i].sellIn < 6) {
if (items[i].quality < 50) {
items[i].quality = items[i].quality + 1;
}
}
}
}
switch (itemName) {
case AGED_BRIE:
qualityUpdater = qualityUpdaters.get(AGED_BRIE);
break;
case BACKSTAGE_PASSES:
qualityUpdater = qualityUpdaters.get(BACKSTAGE_PASSES);
break;
case SULFURAS_HAND_RANGAROS:
// nothing has to be done in that case - this is immutable object
break;
case CONJURED:
qualityUpdater = qualityUpdaters.get(CONJURED);
break;
default:
qualityUpdater = qualityUpdaters.get(GENERAL);
}
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;
}
}
if (qualityUpdater != null) {
List<Item> properItems =
items.stream().filter(item -> itemName.equals(ItemName.resolve(item.getName()))).collect(Collectors.toList());
qualityUpdater.updateQuality(properItems);
}
}
}

View File

@ -1,21 +1,14 @@
package com.gildedrose;
import lombok.AllArgsConstructor;
import lombok.Data;
@Data
@AllArgsConstructor
public class Item {
public String name;
private String name;
private int sellIn;
private int quality;
public int sellIn;
public int quality;
public Item(String name, int sellIn, int quality) {
this.name = name;
this.sellIn = sellIn;
this.quality = quality;
}
@Override
public String toString() {
return this.name + ", " + this.sellIn + ", " + this.quality;
}
}

View File

@ -0,0 +1,18 @@
package com.gildedrose.enums;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
@Getter
@RequiredArgsConstructor
public enum BorderDays {
INCREASE_QUALITY_BY_TWO_WHEN_LESS_THEN_OR_EQUAL_TEN_DAYS(10),
INCREASE_QUALITY_BY_THREE_WHEN_LESS_THEN_OR_EQUAL_FIVE_DAYS(5),
INCREASE_BY_TWO_DAYS(2),
INCREASE_BY_THREE_DAYS(3),
DATE_HAS_PASSED(0);
private final int days;
}

View File

@ -0,0 +1,15 @@
package com.gildedrose.enums;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
@Getter
@RequiredArgsConstructor
public enum BorderQualities {
MIN_QUALITY(0),
MAX_QUALITY(50);
private final int quality;
}

View File

@ -0,0 +1,27 @@
package com.gildedrose.enums;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import java.util.Set;
@Getter
@RequiredArgsConstructor
public enum ItemName {
BACKSTAGE_PASSES("Backstage passes to a TAFKAL80ETC concert"),
SULFURAS_HAND_RANGAROS("Sulfuras, Hand of Ragnaros"),
AGED_BRIE("Aged Brie"),
GENERAL("General item"),
CONJURED("Conjured Mana Cake");
private final String name;
public static ItemName resolve(String name) {
return Set.of(ItemName.values()).stream()
.filter(itemName -> name.equalsIgnoreCase(itemName.getName()))
.findFirst()
.orElse(GENERAL);
}
}

View File

@ -0,0 +1,32 @@
package com.gildedrose.service;
import com.gildedrose.Item;
import lombok.NoArgsConstructor;
import java.util.List;
import static com.gildedrose.enums.BorderQualities.MAX_QUALITY;
import static com.gildedrose.enums.BorderQualities.MIN_QUALITY;
@NoArgsConstructor
public class AgedBrieQualityUpdater implements QualityUpdater {
@Override
public void updateQuality(List<Item> items) {
for (Item item : items) {
item.setSellIn(item.getSellIn() - 1);
// quantity actually always increases as the item becomes older
if (item.getQuality() < MAX_QUALITY.getQuality() && item.getQuality() >= MIN_QUALITY.getQuality()) {
// Once the sell by date has passed, quality degrades twice as fast - should this have to happen, because it is logical contradiction ???
/*if (item.getSellIn() < DATE_HAS_PASSED.getDays()) {
item.setQuality(item.getQuality() / 2);
} else {*/
if (item.getSellIn() < 0) {
item.setQuality(item.getQuality() + 2);
} else {
item.setQuality(item.getQuality() + 1);
}
}
}
}
}

View File

@ -0,0 +1,44 @@
package com.gildedrose.service;
import com.gildedrose.Item;
import lombok.NoArgsConstructor;
import java.util.List;
import static com.gildedrose.enums.BorderDays.DATE_HAS_PASSED;
import static com.gildedrose.enums.BorderDays.INCREASE_BY_THREE_DAYS;
import static com.gildedrose.enums.BorderDays.INCREASE_BY_TWO_DAYS;
import static com.gildedrose.enums.BorderDays.INCREASE_QUALITY_BY_THREE_WHEN_LESS_THEN_OR_EQUAL_FIVE_DAYS;
import static com.gildedrose.enums.BorderDays.INCREASE_QUALITY_BY_TWO_WHEN_LESS_THEN_OR_EQUAL_TEN_DAYS;
import static com.gildedrose.enums.BorderQualities.MAX_QUALITY;
import static com.gildedrose.enums.BorderQualities.MIN_QUALITY;
@NoArgsConstructor
public class BackstagePassesQualityUpdater implements QualityUpdater {
@Override
public void updateQuality(List<Item> items) {
for (Item item : items) {
item.setSellIn(item.getSellIn() - 1);
if (item.getSellIn() < DATE_HAS_PASSED.getDays()) {
item.setQuality(0);
}
// quantity actually always increases as the item becomes older (if sellIn < 0 decreases twice by general condition!)
if (item.getQuality() < MAX_QUALITY.getQuality() && item.getQuality() > MIN_QUALITY.getQuality()) {
// Once the sell by date has passed, quality degrades twice as fast - - should this have to happen, because it is logical contradiction ???
if (item.getSellIn() >= INCREASE_QUALITY_BY_THREE_WHEN_LESS_THEN_OR_EQUAL_FIVE_DAYS.getDays() &&
item.getSellIn() < INCREASE_QUALITY_BY_TWO_WHEN_LESS_THEN_OR_EQUAL_TEN_DAYS.getDays() &&
item.getQuality() + INCREASE_BY_TWO_DAYS.getDays() <= MAX_QUALITY.getQuality()) {
item.setQuality(item.getQuality() + INCREASE_BY_TWO_DAYS.getDays());
} else if (item.getSellIn() < INCREASE_QUALITY_BY_THREE_WHEN_LESS_THEN_OR_EQUAL_FIVE_DAYS.getDays() &&
item.getQuality() + INCREASE_BY_THREE_DAYS.getDays() <= MAX_QUALITY.getQuality()) {
item.setQuality(item.getQuality() + INCREASE_BY_THREE_DAYS.getDays());
} else {
item.setQuality(item.getQuality() + 1);
}
}
}
}
}

View File

@ -0,0 +1,24 @@
package com.gildedrose.service;
import com.gildedrose.Item;
import lombok.NoArgsConstructor;
import java.util.List;
import static com.gildedrose.enums.BorderQualities.MAX_QUALITY;
import static com.gildedrose.enums.BorderQualities.MIN_QUALITY;
@NoArgsConstructor
public class ConjuredQualityUpdater implements QualityUpdater {
@Override
public void updateQuality(List<Item> items) {
for (Item item : items) {
item.setSellIn(item.getSellIn() - 1);
if (item.getQuality() < MAX_QUALITY.getQuality() &&
item.getQuality() > MIN_QUALITY.getQuality() &&
item.getQuality() - 2 >= MIN_QUALITY.getQuality()) {
item.setQuality(item.getQuality() - 2);
}
}
}
}

View File

@ -0,0 +1,28 @@
package com.gildedrose.service;
import com.gildedrose.Item;
import lombok.NoArgsConstructor;
import java.util.List;
import static com.gildedrose.enums.BorderDays.DATE_HAS_PASSED;
import static com.gildedrose.enums.BorderQualities.MAX_QUALITY;
import static com.gildedrose.enums.BorderQualities.MIN_QUALITY;
@NoArgsConstructor
public class GeneralQualityUpdater implements QualityUpdater {
@Override
public void updateQuality(List<Item> items) {
for (Item item : items) {
item.setSellIn(item.getSellIn() - 1);
// at the end of each day decrease quantity and sellIn (quality > 0 and quantity <= 50 always)
if (item.getSellIn() < DATE_HAS_PASSED.getDays() &&
item.getQuality() - 2 >= MIN_QUALITY.getQuality()) {
item.setQuality(item.getQuality() - 2);
} else if (item.getQuality() < MAX_QUALITY.getQuality() && item.getQuality() > MIN_QUALITY.getQuality()) {
item.setQuality(item.getQuality() - 1);
}
}
}
}

View File

@ -0,0 +1,11 @@
package com.gildedrose.service;
import com.gildedrose.Item;
import java.util.List;
public interface QualityUpdater {
void updateQuality(List<Item> items);
}

View File

@ -1,6 +1,12 @@
package com.gildedrose;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import java.util.List;
import java.util.stream.Stream;
import static org.junit.jupiter.api.Assertions.assertEquals;
@ -8,10 +14,125 @@ class GildedRoseTest {
@Test
void foo() {
Item[] items = new Item[] { new Item("foo", 0, 0) };
List<Item> items = List.of(new Item("foo", 0, 0));
GildedRose app = new GildedRose(items);
app.updateQuality();
assertEquals("fixme", app.items[0].name);
assertEquals("foo", app.getItems().get(0).getName());
}
@ParameterizedTest
@MethodSource("agedBrieQualities")
void testAgedBrieQualityUpdater(String name, int sellIn, int quality) {
List<Item> items = List.of(new Item(name, sellIn, quality));
GildedRose app = new GildedRose(items);
for (int i = 0; i <= sellIn; i++) {
app.updateQuality();
}
for (Item item : items) {
assertEquals(name, item.getName());
assertEquals(quality + sellIn + 2, item.getQuality());
}
}
private static Stream<Arguments> agedBrieQualities() {
return Stream.of(
Arguments.of("Aged Brie", 2, 0),
Arguments.of("Aged Brie", 5, 10));
}
@ParameterizedTest
@MethodSource("backstagePassesQualities")
void testBackstagePassesQualityUpdater(String name, int sellIn, int quality) {
List<Item> items = List.of(new Item(name, sellIn, quality));
GildedRose app = new GildedRose(items);
for (int i = 0; i < sellIn; i++) {
app.updateQuality();
}
for (Item item : items) {
assertEquals(name, item.getName());
assertEquals(50 - item.getSellIn() * 5, item.getQuality());
}
}
private static Stream<Arguments> backstagePassesQualities() {
return Stream.of(
Arguments.of("Backstage passes to a TAFKAL80ETC concert", 15, 20),
Arguments.of("Backstage passes to a TAFKAL80ETC concert", 10, 49),
Arguments.of("Backstage passes to a TAFKAL80ETC concert", 5, 49));
}
@ParameterizedTest
@MethodSource("sulfurasQualities")
void testSulfurasQualityUpdater(String name, int sellIn, int quality) {
List<Item> items = List.of(new Item(name, sellIn, quality));
GildedRose app = new GildedRose(items);
for (int i = 0; i < sellIn; i++) {
app.updateQuality();
}
for (Item item : items) {
assertEquals(name, item.getName());
assertEquals(80, item.getQuality());
}
}
private static Stream<Arguments> sulfurasQualities() {
return Stream.of(
Arguments.of("Sulfuras, Hand of Ragnaros", 0, 80),
Arguments.of("Sulfuras, Hand of Ragnaros", -1, 80),
Arguments.of("Sulfuras, Hand of Ragnaros", 5, 80));
}
@ParameterizedTest
@MethodSource("conjuredQualities")
void testConjuredQualityUpdater(String name, int sellIn, int quality) {
List<Item> items = List.of(new Item(name, sellIn, quality));
GildedRose app = new GildedRose(items);
for (int i = 0; i < sellIn; i++) {
app.updateQuality();
}
for (Item item : items) {
assertEquals(name, item.getName());
assertEquals(Math.max(quality - sellIn * 2, 0), item.getQuality());
}
}
private static Stream<Arguments> conjuredQualities() {
return Stream.of(
Arguments.of("Conjured Mana Cake", 3, 6),
Arguments.of("Conjured Mana Cake", 2, 5),
Arguments.of("Conjured Mana Cake", 7, 10));
}
@ParameterizedTest
@MethodSource("generalQualities")
void testGeneralQualityUpdater(String name, int sellIn, int quality) {
List<Item> items = List.of(new Item(name, sellIn, quality));
GildedRose app = new GildedRose(items);
for (int i = 0; i < sellIn; i++) {
app.updateQuality();
}
for (Item item : items) {
assertEquals(name, item.getName());
if (item.getSellIn() < 0) {
assertEquals(quality - sellIn * 2, item.getQuality());
} else {
assertEquals(quality - sellIn, item.getQuality());
}
}
}
private static Stream<Arguments> generalQualities() {
return Stream.of(
Arguments.of("+5 Dexterity Vest", 10, 20),
Arguments.of("Elixir of the Mongoose", 5, 7));
}
}

View File

@ -1,10 +1,15 @@
package com.gildedrose;
public class TexttestFixture {
public static void main(String[] args) {
System.out.println("OMGHAI!");
import lombok.extern.slf4j.Slf4j;
Item[] items = new Item[] {
import java.util.List;
@Slf4j
public class TextTestFixture {
public static void main(String[] args) {
log.info("OMGHAI!");
List<Item> items = List.of(
new Item("+5 Dexterity Vest", 10, 20), //
new Item("Aged Brie", 2, 0), //
new Item("Elixir of the Mongoose", 5, 7), //
@ -14,22 +19,21 @@ public class TexttestFixture {
new Item("Backstage passes to a TAFKAL80ETC concert", 10, 49),
new Item("Backstage passes to a TAFKAL80ETC concert", 5, 49),
// this conjured item does not work properly yet
new Item("Conjured Mana Cake", 3, 6) };
new Item("Conjured Mana Cake", 3, 6));
GildedRose app = new GildedRose(items);
int days = 2;
int days = 12;
if (args.length > 0) {
days = Integer.parseInt(args[0]) + 1;
}
for (int i = 0; i < days; i++) {
System.out.println("-------- day " + i + " --------");
System.out.println("name, sellIn, quality");
log.info("-------- day {} --------", i);
log.info("name, sellIn, quality");
for (Item item : items) {
System.out.println(item);
log.info(item.toString());
}
System.out.println();
app.updateQuality();
}
}