refactored with strategy pattern + factory and added test cases

This commit is contained in:
Martin 2022-02-25 16:15:33 +01:00
parent f80413c2fd
commit 2332d4e30e
12 changed files with 167 additions and 80 deletions

View File

@ -1,69 +1,11 @@
export class Item {
name: string;
sellIn: number;
quality: number;
constructor(name, sellIn, quality) {
this.name = name;
this.sellIn = sellIn;
this.quality = quality;
}
}
import Item from "./models/Item";
import { UpdateStrategyFactory } from "./updateStrategyFactory";
export class GildedRose {
items: Array<Item>;
private constructor() {}
constructor(items = [] as Array<Item>) {
this.items = items;
}
updateQuality() {
for (let i = 0; i < this.items.length; i++) {
if (this.items[i].name != 'Aged Brie' && this.items[i].name != 'Backstage passes to a TAFKAL80ETC concert') {
if (this.items[i].quality > 0) {
if (this.items[i].name != 'Sulfuras, Hand of Ragnaros') {
this.items[i].quality = this.items[i].quality - 1
}
}
} else {
if (this.items[i].quality < 50) {
this.items[i].quality = this.items[i].quality + 1
if (this.items[i].name == 'Backstage passes to a TAFKAL80ETC concert') {
if (this.items[i].sellIn < 11) {
if (this.items[i].quality < 50) {
this.items[i].quality = this.items[i].quality + 1
}
}
if (this.items[i].sellIn < 6) {
if (this.items[i].quality < 50) {
this.items[i].quality = this.items[i].quality + 1
}
}
}
}
}
if (this.items[i].name != 'Sulfuras, Hand of Ragnaros') {
this.items[i].sellIn = this.items[i].sellIn - 1;
}
if (this.items[i].sellIn < 0) {
if (this.items[i].name != 'Aged Brie') {
if (this.items[i].name != 'Backstage passes to a TAFKAL80ETC concert') {
if (this.items[i].quality > 0) {
if (this.items[i].name != 'Sulfuras, Hand of Ragnaros') {
this.items[i].quality = this.items[i].quality - 1
}
}
} else {
this.items[i].quality = this.items[i].quality - this.items[i].quality
}
} else {
if (this.items[i].quality < 50) {
this.items[i].quality = this.items[i].quality + 1
}
}
}
}
return this.items;
static updateQuality(items = [] as Array<Item>) {
let updatedItems = items.map(
item => UpdateStrategyFactory.getUpdateStrategy(item).updateItem(item));
return updatedItems;
}
}

View File

@ -0,0 +1,11 @@
export default class Item {
name: string;
sellIn: number;
quality: number;
constructor(name, sellIn, quality) {
this.name = name;
this.sellIn = sellIn;
this.quality = quality;
}
}

View File

@ -0,0 +1,12 @@
import Item from "./../models/Item";
import IUpdateStrategy from "./IUpdateStrategy";
export default class AgedBrieStrategy implements IUpdateStrategy {
constructor(private qualityFactor : number) {}
updateItem(item: Item) : Item {
let quality = Math.min(50, item.quality + 1 * this.qualityFactor);
let sellIn = item.sellIn - 1;
return new Item(item.name, sellIn, quality);
}
}

View File

@ -0,0 +1,20 @@
import Item from "./../models/Item";
import IUpdateStrategy from "./IUpdateStrategy";
export default class BackstagePassStrategy implements IUpdateStrategy {
updateItem(item: Item) : Item {
let increaseFactor = this.getIncreaseFactor(item.sellIn);
let newQuality =
increaseFactor
? Math.min(50, item.quality + increaseFactor)
: 0;
let newSellIn = item.sellIn - 1;
return new Item(item.name, newSellIn, newQuality);
}
private getIncreaseFactor(sellIn: number){
if (sellIn > 10) return 1;
if (sellIn <= 10 && sellIn > 5) return 2;
if (sellIn <= 5 && sellIn > 0) return 3;
}
}

View File

@ -0,0 +1,13 @@
import Item from "./../models/Item";
import IUpdateStrategy from "./IUpdateStrategy";
export default class DefaultStrategy implements IUpdateStrategy {
constructor(private qualityFactor : number) {}
updateItem(item: Item) : Item {
let newQuality = Math.max(0, item.quality - this.qualityFactor);
let newSellIn = item.sellIn - 1;
return new Item(item.name, newSellIn, newQuality);
}
}

View File

@ -0,0 +1,6 @@
import Item from "./../models/Item";
export default interface IUpdateStrategy {
updateItem(item: Item) : Item;
}

View File

@ -0,0 +1,8 @@
import Item from "@/models/Item";
import IUpdateStrategy from "./IUpdateStrategy";
export default class NoUpdateStrategy implements IUpdateStrategy {
updateItem(item: Item) : Item {
return new Item(item.name, item.sellIn, item.quality);
}
}

View File

@ -0,0 +1,12 @@
import Item from "./../models/Item";
import IUpdateStrategy from "./IUpdateStrategy";
export default class ConjuredStrategy implements IUpdateStrategy {
constructor(private qualityFactor : number) {}
updateItem(item: Item) : Item {
let quality = Math.max(0, item.quality - 2 * this.qualityFactor);
let sellIn = item.sellIn - 1;
return new Item(item.name, sellIn, quality);
}
}

View File

@ -0,0 +1,27 @@
import Item from "./models/Item";
import AgedBrieStrategy from "./updateStrategies/AgedBrieStrategy";
import BackstagePassStrategy from "./updateStrategies/BackstagePassStrategy";
import ConjuredStrategy from "./updateStrategies/conjuredStrategy";
import DefaultStrategy from "./updateStrategies/DefaultStrategy";
import IUpdateStrategy from "./updateStrategies/IUpdateStrategy";
import NoUpdateStrategy from "./updateStrategies/NoUpdateStrategy";
export class UpdateStrategyFactory {
static getUpdateStrategy(item: Item): IUpdateStrategy {
let qualityFactor = item.sellIn <= 0 ? 2 : 1;
switch (item.name) {
case 'Aged Brie':
return new AgedBrieStrategy(qualityFactor);
case 'Sulfuras, Hand of Ragnaros':
return new NoUpdateStrategy();
case 'Backstage passes to a TAFKAL80ETC concert':
return new BackstagePassStrategy();
case 'conjured':
return new ConjuredStrategy(qualityFactor);
default:
return new DefaultStrategy(qualityFactor);
}
}
}

View File

@ -1,4 +1,5 @@
import { Item, GildedRose } from '../app/gilded-rose';
import { GildedRose } from '../app/gilded-rose';
import Item from "../app/models/Item";
const items = [
new Item("+5 Dexterity Vest", 10, 20), //
@ -12,9 +13,6 @@ const items = [
// this conjured item does not work properly yet
new Item("Conjured Mana Cake", 3, 6)];
const gildedRose = new GildedRose(items);
let days: number = 2;
if (process.argv.length > 2) {
days = +process.argv[2];
@ -28,5 +26,5 @@ for (let i = 0; i < days; i++) {
});
console.log();
gildedRose.updateQuality();
GildedRose.updateQuality(items);
}

View File

@ -1,9 +1,46 @@
import { Item, GildedRose } from '@/gilded-rose';
import { GildedRose } from '@/gilded-rose';
import Item from "@/models/Item";
describe('Gilded Rose', () => {
it('should foo', () => {
const gildedRose = new GildedRose([new Item('foo', 0, 0)]);
const items = gildedRose.updateQuality();
expect(items[0].name).toBe('fixme');
});
it.each`
name | quality | expectedQuality | sellIn | expectedSellIn | description
${'abc def'} | ${0} | ${0} | ${8} | ${7} | ${"quality should not be negative"}
${'abc def'} | ${0} | ${0} | ${0} | ${-1} | ${"sellIn should decrease"}
${'abc def'} | ${8} | ${6} | ${0} | ${-1} | ${"quality should decrease twice after sellIn is negative"}
${'Aged Brie'} | ${0} | ${1} | ${8} | ${7} | ${"should increase quality by 1"}
${'Aged Brie'} | ${0} | ${2} | ${0} | ${-1} | ${"should increase quality by 1"}
${'Aged Brie'} | ${50} | ${50} | ${8} | ${7} | ${"quality should not be higher than 50"}
${'conjured'} | ${0} | ${0} | ${8} | ${7} | ${"quality should not be negative"}
${'conjured'} | ${0} | ${0} | ${0} | ${-1} | ${"quality should not be negative"}
${'conjured'} | ${4} | ${2} | ${8} | ${7} | ${"quality should decrease by 2"}
${'conjured'} | ${4} | ${0} | ${0} | ${-1} | ${"quality should decrease by 4 when sellIn is <= 0"}
${'Sulfuras, Hand of Ragnaros'} | ${10} | ${10} | ${10} | ${10} | ${"should not change "}
${'Backstage passes to a TAFKAL80ETC concert'} | ${10}| ${11}| ${11} | ${10} | ${"quality increases by 1 for sellIn > 10"}
${'Backstage passes to a TAFKAL80ETC concert'} | ${10}| ${11}| ${30} | ${29} | ${"quality increases by 1 for sellIn > 10"}
${'Backstage passes to a TAFKAL80ETC concert'} | ${10}| ${12}| ${10} | ${9} | ${"quality increases by 2 for sellIn <= 10 && > 5"}
${'Backstage passes to a TAFKAL80ETC concert'} | ${10}| ${12}| ${6} | ${5} | ${"quality increases by 2 for sellIn <= 10 && > 5"}
${'Backstage passes to a TAFKAL80ETC concert'} | ${10}| ${13}| ${5} | ${4} | ${"quality increases by 3 for sellIn <= 5 && >= 0"}
${'Backstage passes to a TAFKAL80ETC concert'} | ${10}| ${13}| ${1} | ${0} | ${"quality increases by 3 for sellIn <= 5 && > 0"}
${'Backstage passes to a TAFKAL80ETC concert'} | ${10}| ${0} | ${0} | ${-1} | ${"quality is 0 if sellIn is exceeded"}
${'Backstage passes to a TAFKAL80ETC concert'} | ${10}| ${0} | ${-1} | ${-2} | ${"quality is 0 if sellIn is exceeded"}
`("'$description' for '$name' with quality '$quality' and sellIn '$sellIn'", ({name, quality, expectedQuality, sellIn, expectedSellIn, description}) => {
const item1 = new Item(name, sellIn, quality);
const items = GildedRose.updateQuality([item1]);
expect(items[0].name).toBe(name);
expect(items[0].quality).toBe(expectedQuality);
expect(items[0].sellIn).toBe(expectedSellIn);
});
it("should be correct for 2 items", () => {
const item1 = new Item('abc def', 8, 5);
const item2 = new Item('Aged Brie', 10, 2);
const items = GildedRose.updateQuality([item1, item2]);
expect(items[0].name).toBe(item1.name);
expect(items[0].quality).toBe(4);
expect(items[0].sellIn).toBe(7);
expect(items[1].name).toBe(item2.name);
expect(items[1].quality).toBe(3);
expect(items[1].sellIn).toBe(9);
});
});

View File

@ -1,10 +1,11 @@
import { expect } from 'chai';
import { Item, GildedRose } from '@/gilded-rose';
import { GildedRose } from '@/gilded-rose';
import Item from "@/models/Item";
describe('Gilded Rose', () => {
it('should foo', () => {
const gildedRose = new GildedRose([new Item('foo', 0, 0)]);
const items = gildedRose.updateQuality();
expect(items[0].name).to.equal('fixme');
// const gildedRose = new GildedRose([new Item('foo', 0, 0)]);
// const items = gildedRose.updateQuality();
expect("items[0].name").to.equal('fixme');
});
});