├── .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 | --------------------------------------------------------------------------------