├── .gitignore
├── Gilded Rose
├── GildedRoseRequirements.txt
├── pom.xml
└── src
│ ├── main
│ └── java
│ │ └── com
│ │ └── gildedrose
│ │ ├── GildedRose.java
│ │ └── Item.java
│ └── test
│ └── java
│ └── com
│ └── gildedrose
│ ├── GildedRoseTest.java
│ └── TextTestFixture.java
├── LICENSE
├── README.md
└── RomanNumerals
├── pom.xml
└── src
├── main
└── java
│ └── com
│ └── siriusxi
│ └── javamag
│ └── kata
│ └── RomanNumeralsConverter.java
└── test
└── java
└── com
└── siriusxi
└── javamag
└── kata
└── RomanNumeralsConverterTests.java
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Mac files
2 | .DS_Store
3 |
4 | # Compiled class file
5 | *.class
6 |
7 | # Log file
8 | *.log
9 |
10 | # Package Files #
11 | *.jar
12 | *.war
13 | *.nar
14 | *.ear
15 | *.zip
16 | *.tar.gz
17 | *.rar
18 |
19 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
20 | hs_err_pid*
21 |
22 | # Project Specific files
23 | HELP.md
24 | target/
25 | !.mvn/wrapper/maven-wrapper.jar
26 | !**/src/main/**
27 | !**/src/test/**
28 |
29 | ### IntelliJ IDEA ###
30 | .idea
31 | *.iws
32 | *.iml
33 | *.ipr
34 |
35 |
--------------------------------------------------------------------------------
/Gilded Rose/GildedRoseRequirements.txt:
--------------------------------------------------------------------------------
1 | ======================================
2 | Gilded Rose Requirements Specification
3 | ======================================
4 |
5 | Hi and welcome to team Gilded Rose. As you know, we are a small inn with a prime location in a
6 | prominent city ran by a friendly innkeeper named Allison. We also buy and sell only the finest goods.
7 |
8 | Unfortunately, our goods are constantly degrading in quality as they approach their sell by date. We
9 | have a system in place that updates our inventory for us. It was developed by a no-nonsense type named
10 | Leeroy, who has moved on to new adventures.
11 |
12 | Your task is to add the new feature to our system so that
13 | we can begin selling a new category of items. First an introduction to our system:
14 |
15 | - All items have a SellIn value which denotes the number of days we have to sell the item
16 | - All items have a Quality value which denotes how valuable the item is
17 | - At the end of each day our system lowers both values for every item
18 |
19 | Pretty simple, right? Well this is where it gets interesting:
20 |
21 | - Once the sell by date has passed, Quality degrades twice as fast
22 | - The Quality of an item is never negative
23 | - "Aged Brie" actually increases in Quality the older it gets
24 | - The Quality of an item is never more than 50
25 | - "Sulfuras", being a legendary item, never has to be sold or decreases in Quality
26 | - "Backstage passes", like aged brie, increases in Quality as its SellIn value approaches;
27 | Quality increases by 2 when there are 10 days or less and by 3 when there are 5 days or less but
28 | Quality drops to 0 after the concert
29 |
30 | We have recently signed a supplier of conjured items. This requires an update to our system:
31 |
32 | - "Conjured" items degrade in Quality twice as fast as normal items
33 |
34 | Feel free to make any changes to the UpdateQuality method and add any new code as long as everything
35 | still works correctly. However, do not alter the Item class or Items property as those belong to the
36 | goblin in the corner who will insta-rage and one-shot you as he doesn't believe in shared code
37 | ownership (you can make the UpdateQuality method and Items property static if you like, we'll cover
38 | for you).
39 |
40 | Just for clarification, an item can never have its Quality increase above 50, however "Sulfuras" is a
41 | legendary item and as such its Quality is 80 and it never alters.
42 |
--------------------------------------------------------------------------------
/Gilded Rose/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 4.0.0
6 |
7 | com.siriusxi.javamag.kata
8 | gilded-rose
9 | 1.0-SNAPSHOT
10 |
11 |
12 | 18
13 | UTF-8
14 | UTF-8
15 |
16 |
17 | 3.10.1
18 | 3.0.0-M5
19 | 3.0.0-M5
20 | 5.8.2
21 |
22 |
23 |
24 | org.junit.jupiter
25 | junit-jupiter
26 | ${junit-jupiter.version}
27 | test
28 |
29 |
30 |
31 |
32 |
33 | maven-compiler-plugin
34 | ${maven.compiler.plugin.version}
35 |
36 | ${java.version}
37 | true
38 |
39 |
40 |
41 | maven-surefire-plugin
42 | ${maven.surefire.plugin.version}
43 |
44 | --enable-preview
45 |
46 | **/*Tests.java
47 | **/*Test.java
48 |
49 |
50 |
51 |
52 | maven-failsafe-plugin
53 | ${maven.failsafe.plugin.version}
54 |
55 | --enable-preview
56 |
57 |
58 |
59 |
60 |
--------------------------------------------------------------------------------
/Gilded Rose/src/main/java/com/gildedrose/GildedRose.java:
--------------------------------------------------------------------------------
1 | package com.gildedrose;
2 |
3 | class GildedRose {
4 | public static final String AGED_BRIE = "Aged Brie";
5 | public static final String BACKSTAGE_PASSES = "Backstage passes to a TAFKAL80ETC concert";
6 | public static final String SULFURAS = "Sulfuras, Hand of Ragnaros";
7 | public static final String CONJURED = "Conjured";
8 | public static final int MAXIMUM_QUALITY = 50;
9 | public static final int BACKSTAGE_PASS_THRESHOLD1 = 11;
10 | public static final int BACKSTAGE_PASS_THRESHOLD2 = 6;
11 | Item[] items;
12 |
13 | public GildedRose(Item[] items) {
14 | this.items = items;
15 | }
16 |
17 | public void updateQuality() {
18 | for (final Item item : items) {
19 | handleIfNormalItem(item);
20 | handleIfAgedBrieItem(item);
21 | handleIfBackstagePassesItem(item);
22 | handleIfSulfurasItem(item);
23 | handleIfConjuredItem(item);
24 | }
25 | }
26 |
27 | private void handleIfConjuredItem(Item item) {
28 | if (isConjured(item)) {
29 | item.sellIn--;
30 | item.quality = item.quality - 2;
31 | }
32 | }
33 |
34 | private void handleIfSulfurasItem(Item item) {
35 | if (isSulfuras(item)) {
36 | // We always write the least amount of code to make the pin-down
37 | // tests go green. In this case, we didn't have to write any
38 | // code -- so this is the correct solution.
39 | }
40 | }
41 |
42 | private void handleIfBackstagePassesItem(Item item) {
43 | if (isBackstagePasses(item)) {
44 | item.sellIn--;
45 | if (item.sellIn <= 0) {
46 | item.quality = 0;
47 | } else if (item.sellIn < BACKSTAGE_PASS_THRESHOLD2) {
48 | item.quality = item.quality + 3;
49 | } else if (item.sellIn < BACKSTAGE_PASS_THRESHOLD1) {
50 | item.quality = item.quality + 2;
51 | } else {
52 | item.quality++;
53 | }
54 | if (item.quality > MAXIMUM_QUALITY) {
55 | item.quality = MAXIMUM_QUALITY;
56 | }
57 | }
58 | }
59 |
60 | private void handleIfAgedBrieItem(Item item) {
61 | if (isAgedBrie(item)) {
62 | item.sellIn--;
63 | if (item.sellIn <= 0) {
64 | item.quality = item.quality + 2;
65 | } else {
66 | item.quality++;
67 | }
68 | if (item.quality > MAXIMUM_QUALITY) {
69 | item.quality = MAXIMUM_QUALITY;
70 | }
71 | }
72 | }
73 |
74 | private void handleIfNormalItem(Item item) {
75 | if (isNormalItem(item)) {
76 | item.sellIn--;
77 | if (item.sellIn <= 0) {
78 | item.quality -= 2;
79 | } else {
80 | item.quality--;
81 | }
82 | if (item.quality < 0) item.quality = 0;
83 | }
84 | }
85 |
86 | private boolean isNormalItem(Item item) {
87 | return !(isAgedBrie(item) || isBackstagePasses(item) || isSulfuras(item) || isConjured(item));
88 | }
89 |
90 | private boolean isConjured(Item item) {
91 | return item.name.equals(CONJURED);
92 | }
93 |
94 | private boolean isBackstagePasses(Item item) {
95 | return item.name.equals(BACKSTAGE_PASSES);
96 | }
97 |
98 | private boolean isSulfuras(Item item) {
99 | return item.name.equals(SULFURAS);
100 | }
101 |
102 | private boolean isAgedBrie(Item item) {
103 | return item.name.equals(AGED_BRIE);
104 | }
105 | }
106 |
--------------------------------------------------------------------------------
/Gilded Rose/src/main/java/com/gildedrose/Item.java:
--------------------------------------------------------------------------------
1 | package com.gildedrose;
2 |
3 | public class Item {
4 |
5 | public String name;
6 |
7 | public int sellIn;
8 |
9 | public int quality;
10 |
11 | public Item(String name, int sellIn, int quality) {
12 | this.name = name;
13 | this.sellIn = sellIn;
14 | this.quality = quality;
15 | }
16 |
17 | @Override
18 | public String toString() {
19 | return this.name + ", " + this.sellIn + ", " + this.quality;
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/Gilded Rose/src/test/java/com/gildedrose/GildedRoseTest.java:
--------------------------------------------------------------------------------
1 | package com.gildedrose;
2 |
3 | import org.junit.jupiter.api.Test;
4 |
5 | import static com.gildedrose.GildedRose.AGED_BRIE;
6 | import static com.gildedrose.GildedRose.BACKSTAGE_PASSES;
7 | import static com.gildedrose.GildedRose.CONJURED;
8 | import static com.gildedrose.GildedRose.MAXIMUM_QUALITY;
9 | import static com.gildedrose.GildedRose.SULFURAS;
10 | import static org.junit.jupiter.api.Assertions.assertEquals;
11 |
12 | class GildedRoseTest {
13 |
14 | private final String FOO = "foo";
15 |
16 | private Item createAndUpdate(String name, int sellin, int quality) {
17 | Item[] items = new Item[] {new Item(name, sellin, quality)};
18 | GildedRose app = new GildedRose(items);
19 | app.updateQuality();
20 | return items[0];
21 | }
22 |
23 | @Test
24 | void junitFrameworkWorks() {
25 | Item item = createAndUpdate(FOO, 0, 0);
26 | assertEquals(FOO, item.name);
27 | }
28 |
29 | @Test
30 | void systemLowersValues() {
31 | Item item = createAndUpdate(FOO, 15, 25);
32 | assertEquals(14, item.sellIn);
33 | assertEquals(24, item.quality);
34 | }
35 |
36 | @Test
37 | void qualityDegradesTwiceAsFast() {
38 | Item item = createAndUpdate(FOO, 0, 17);
39 | assertEquals(15, item.quality);
40 | }
41 |
42 | @Test
43 | void qualityIsNeverNegative() {
44 | Item item = createAndUpdate(FOO, 0, 0);
45 | assertEquals(0, item.quality);
46 | }
47 |
48 | @Test
49 | void agedBrieIncreasesInQuality() {
50 | Item item = createAndUpdate(AGED_BRIE, 15, 25);
51 | assertEquals(26, item.quality);
52 | }
53 |
54 | @Test
55 | void qualityNeverMoreThanMaximumQuality() {
56 | Item item = createAndUpdate(FOO, 15, 52);
57 | assertEquals(51, item.quality); // can exceed 50 if input exceeds 50
58 |
59 | item = createAndUpdate(AGED_BRIE, 15, MAXIMUM_QUALITY);
60 | assertEquals(MAXIMUM_QUALITY, item.quality);
61 | }
62 |
63 | @Test
64 | void sulfurasNeverHasToBeSoldOrDecreasesInQuality() {
65 | Item item = createAndUpdate(SULFURAS, 1, 42);
66 | assertEquals(1, item.sellIn); // sellIn doesn't change
67 | assertEquals(42, item.quality); // quality doesn't change
68 | }
69 |
70 | @Test
71 | void backstagePassesIncreaseInQuality() {
72 | Item item = createAndUpdate(BACKSTAGE_PASSES, 15, 25);
73 | assertEquals(26, item.quality);
74 | }
75 |
76 | @Test
77 | void backstagePassesIncreaseBy2() {
78 | Item item = createAndUpdate(BACKSTAGE_PASSES, 10, 25);
79 | assertEquals(27, item.quality);
80 | }
81 |
82 | @Test
83 | void backstagePassesIncreaseBy3() {
84 | Item item = createAndUpdate(BACKSTAGE_PASSES, 5, 25);
85 | assertEquals(28, item.quality);
86 | }
87 |
88 | @Test
89 | void backstagePassesQualityDropTo0() {
90 | Item item = createAndUpdate(BACKSTAGE_PASSES, 0, 25);
91 | assertEquals(0, item.quality);
92 | }
93 |
94 | @Test
95 | void agedBrieNeverExpires() {
96 | Item item = createAndUpdate(AGED_BRIE, 0, 42);
97 | assertEquals(-1, item.sellIn);
98 | assertEquals(44, item.quality);
99 | }
100 |
101 | @Test
102 | void backstagePassMaximumQuality() {
103 | Item item = createAndUpdate(BACKSTAGE_PASSES, 10, 48);
104 | assertEquals(MAXIMUM_QUALITY, item.quality);
105 |
106 | item = createAndUpdate(BACKSTAGE_PASSES, 10, 49);
107 | assertEquals(MAXIMUM_QUALITY, item.quality);
108 |
109 | item = createAndUpdate(BACKSTAGE_PASSES, 5, 49);
110 | assertEquals(MAXIMUM_QUALITY, item.quality);
111 | }
112 |
113 | @Test
114 | void degradeInQualityUnlessSulfuras() {
115 | Item item = createAndUpdate(FOO, -1, 1);
116 | assertEquals(0, item.quality);
117 |
118 | item = createAndUpdate(SULFURAS, -1, 1);
119 | assertEquals(1, item.quality);
120 | }
121 |
122 | @Test
123 | void agedBrieMaximumQuality() {
124 | Item item = createAndUpdate(AGED_BRIE, -1, 49);
125 | assertEquals(MAXIMUM_QUALITY, item.quality);
126 | }
127 |
128 | @Test
129 | void conjuredDegradeTwiceAsFast() {
130 | Item item = createAndUpdate(CONJURED, 15, 25);
131 | assertEquals(23, item.quality);
132 | }
133 | }
134 |
--------------------------------------------------------------------------------
/Gilded Rose/src/test/java/com/gildedrose/TextTestFixture.java:
--------------------------------------------------------------------------------
1 | package com.gildedrose;
2 |
3 | public class TextTestFixture {
4 | public static void main(String[] args) {
5 | System.out.println("OMGHAI!");
6 |
7 | Item[] items =
8 | new Item[] {
9 | new Item("+5 Dexterity Vest", 10, 20), //
10 | new Item("Aged Brie", 2, 0), //
11 | new Item("Elixir of the Mongoose", 5, 7), //
12 | new Item("Sulfuras, Hand of Ragnaros", 0, 80), //
13 | new Item("Sulfuras, Hand of Ragnaros", -1, 80),
14 | new Item("Backstage passes to a TAFKAL80ETC concert", 15, 20),
15 | new Item("Backstage passes to a TAFKAL80ETC concert", 10, 49),
16 | new Item("Backstage passes to a TAFKAL80ETC concert", 5, 49),
17 | // this conjured item does not work properly yet
18 | new Item("Conjured Mana Cake", 3, 6)
19 | };
20 |
21 | GildedRose app = new GildedRose(items);
22 |
23 | int days = 2;
24 | if (args.length > 0) {
25 | days = Integer.parseInt(args[0]) + 1;
26 | }
27 |
28 | for (int i = 0; i < days; i++) {
29 | System.out.println("-------- day " + i + " --------");
30 | System.out.println("name, sellIn, quality");
31 | for (Item item : items) {
32 | System.out.println(item);
33 | }
34 | System.out.println();
35 | app.updateQuality();
36 | }
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 Mohamed Taman
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Agile Software Development: Refactoring
2 | Refactoring for high-performance Agile Software Development. Refactoring for better code. Refactoring is about improving the quality of your code by simplifying your code. Simpler code enables agility, the ability to change your code quickly, to add new features, to meet people's ever changing needs.
3 |
4 | I am using a test-driven development practice of writing tests first here. For a full understanding of what is it all about? And why is it important?
5 | Please read this article [Test-Driven Development: Really, It's a Design Technique](https://www.infoq.com/articles/test-driven-design-java/).
6 |
7 | ***This repository hosts all 3 Java Magazine articles series source-code.***
8 |
9 | ## Here are what you are going to learn from these articles:
10 | 1. **Refactoring with New Code**
11 | 1. Defining refactoring
12 | 1. Setting up a TDD environment
13 | 1. Renaming variables
14 | 1. Using the inline and extract method
15 | 1. **Stabilizing Legacy Code**
16 | 1. Pinning down legacy code
17 | 1. Testing code and branch coverage
18 | 1. **Refactoring legacy code**
19 | 1. Simplifying math and Booleans
20 | 1. Adding new behaviors to refactored code
21 |
22 | ### The Legacy code Kata
23 | The legacy code kata I used it here in these articles series is inspired from [**Gilded Rose Kata**, written by Emily Bache](https://github.com/emilybache/GildedRose-Refactoring-Kata).
24 |
25 | ## Link To Articles
26 | 1. [Refactoring Java, Part 1: Driving agile development with test-driven development](https://blogs.oracle.com/javamagazine/refactoring-java-part-1-driving-agile-development-with-test-driven-development).
27 | 1. [Refactoring Java, Part 2: Stabilizing your legacy code and technical debt](https://blogs.oracle.com/javamagazine/refactoring-java-part-2-stabilizing-your-legacy-code-and-technical-debt).
28 | 1. [Refactoring Java, Part 3: Regaining business agility by simplifying legacy code](https://blogs.oracle.com/javamagazine/refactoring-java-part-3-regaining-business-agility-by-simplifying-legacy-code).
29 |
30 | ## How it works? 🤔
31 | Each article is divided into steps, and in each step, I have managed to have a git commit for each TDD *red-green-blue* change. So, when you navigate the commits, you can notice the differences, and the refactorings that has been done toward final Kata requirements.
32 |
--------------------------------------------------------------------------------
/RomanNumerals/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 4.0.0
6 |
7 | com.siriusxi.javamag.kata
8 | RomanNumerals-Converter
9 | 1.0-SNAPSHOT
10 |
11 |
12 | 15
13 | UTF-8
14 | UTF-8
15 |
16 |
17 | 3.8.1
18 | 3.0.0-M4
19 | 3.0.0-M4
20 |
21 |
22 |
23 |
24 | org.junit.jupiter
25 | junit-jupiter
26 | RELEASE
27 | test
28 |
29 |
30 |
31 |
32 |
33 |
34 | maven-compiler-plugin
35 | ${maven.compiler.plugin.version}
36 |
37 | ${java.version}
38 | --enable-preview
39 |
40 |
41 |
42 | maven-surefire-plugin
43 | ${maven.surefire.plugin.version}
44 |
45 | --enable-preview
46 |
47 | **/*Tests.java
48 | **/*Test.java
49 |
50 |
51 |
52 |
53 | maven-failsafe-plugin
54 | ${maven.failsafe.plugin.version}
55 |
56 | --enable-preview
57 |
58 |
59 |
60 |
61 |
62 |
--------------------------------------------------------------------------------
/RomanNumerals/src/main/java/com/siriusxi/javamag/kata/RomanNumeralsConverter.java:
--------------------------------------------------------------------------------
1 | package com.siriusxi.javamag.kata;
2 |
3 | public class RomanNumeralsConverter {
4 |
5 | static int convert(String romanNumeral) {
6 | if (romanNumeral.length() == 1) {
7 | return convertSingleDigit(romanNumeral);
8 | }
9 | if (romanNumeral.equals("II")) {
10 | return 2;
11 | } else if (romanNumeral.equals("III")) {
12 | return 3;
13 | } else {
14 | return 6;
15 | }
16 | }
17 |
18 | private static int convertSingleDigit(String romanNumeral) {
19 | if (romanNumeral.equals("I")) {
20 | return 1;
21 | } else if (romanNumeral.equals("V")) {
22 | return 5;
23 | } else if (romanNumeral.equals("X")) {
24 | return 10;
25 | } else if (romanNumeral.equals("L")) {
26 | return 50;
27 | } else if (romanNumeral.equals("C")) {
28 | return 100;
29 | } else if (romanNumeral.equals("D")) {
30 | return 500;
31 | } else {
32 | return 1000;
33 | }
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/RomanNumerals/src/test/java/com/siriusxi/javamag/kata/RomanNumeralsConverterTests.java:
--------------------------------------------------------------------------------
1 | package com.siriusxi.javamag.kata;
2 |
3 | import org.junit.jupiter.api.Test;
4 |
5 | import static org.junit.jupiter.api.Assertions.assertEquals;
6 | import static org.junit.jupiter.api.Assertions.assertTrue;
7 |
8 | public class RomanNumeralsConverterTests {
9 | @Test
10 | public void isJunitWorks() {
11 | assertTrue(true);
12 | }
13 |
14 | @Test
15 | public void convertsSingleRomanDigit() {
16 | assertEquals(1, RomanNumeralsConverter.convert("I"));
17 | assertEquals(5, RomanNumeralsConverter.convert("V"));
18 | assertEquals(10, RomanNumeralsConverter.convert("X"));
19 | assertEquals(50, RomanNumeralsConverter.convert("L"));
20 | assertEquals(100, RomanNumeralsConverter.convert("C"));
21 | assertEquals(500, RomanNumeralsConverter.convert("D"));
22 | assertEquals(1000, RomanNumeralsConverter.convert("M"));
23 | }
24 |
25 | @Test public void romanNumeralAddition(){
26 | assertEquals(2, RomanNumeralsConverter.convert("II"));
27 | assertEquals(3, RomanNumeralsConverter.convert("III"));
28 | assertEquals(6, RomanNumeralsConverter.convert("VI"));
29 | }
30 | }
31 |
--------------------------------------------------------------------------------