mirror of
https://github.com/emilybache/GildedRose-Refactoring-Kata.git
synced 2026-02-14 22:21:20 +00:00
Also added some final notes on this solution and suggestions for further changes if we can change Item
5.9 KiB
5.9 KiB
This file documents my refactoring and design journey while working through in this kata.
NOTES:
- At this stage, I assume that when we're not supposed to change Item or Items, we shouldn't just ignore them and create a parallel object types to use (that include the different DailyUpdate behaviours).
- This code and requirements were simple enough that I could have created the final version, or at least a massive refactoring immediately. However, I wanted to experience and demonstrate the refactoring process by small steps that can be just as easily applied to more complex code.
My refactoring steps:
- Added full test coverage drawing from the requirements and the existing 30 days texttest test.
Some of the tests failed due to bugs I discovered in the code/existing tests (e.g. relying on exact name instead of the mentioned "code word"). I decorated these tests with[Ignore]until I get to a stage in the refactoring where I can easily fix the issues and re-introduce the tests. - small refactorings of the UpdateQuality() method to make it more readable and separate the different (unrelated) handling of the different types.
- Extract Increasing/Decreasing Quality into a method.
- Separated the processing of the different types from each other.
- Used the
Templatedesign pattern to create DailyUpdater abstract class and derived classes to handle the DailyUpdate's steps for the different types of items.
The different derived classes will be moved to their own files in the future. Left them together for now to show the steps. - Created a
Simple Factorymethod to return the appropriate DailyUpdater object for the item type. - Moved DailyUpdaterFactory to its own class.
- Moved the logic of "Once the sell by date has passed, Quality degrades twice as fast" into the DailyUpdater Template class since it's relevant to all types of items.
- Moved Decrease/IncreaseItem byValue to be 1 by default.
- Refactored DailyUpdaterFactory to reuse existing Updaters instead of creating new ones every time.
This could have been done using a dependency injection framework as well (to automatically create and reuse all the updaters). This might actually be the better way to do this. However, I've chosen to use the factory itself to manually manage it using a semi-flyweight pattern. - Moved ItemType and ItemQuality to their own classes to allow re-use
- Seperated the derived DailyUpdater classes into their own files.
Now, adding a new updater won't have to change the existing DailyUpdater file. - Added a new ItemBuilders hierarchy to enforce building valid items (quality limits and special rules for Legendary items). Also added unit tests and refactored the existing tests to use the new builders.
note: if we could change Item, this could have been implemented in the Item's constructor itself and by using Quality TinyType. However, since we can't change Item, the builders gives us a nice decoupled way to validate the items are constructed consistently. - Added the new Conjured items requirements. Due to the previous refactorings above, I only needed to change the following files/classes:
ItemTypeto add the new type enum and definition- Add a new
DailyUpdaterForConjuredItemsupdater to handle the new type's requirements DailyUpdaterFactoryto create and return the new updater
Note that I didn't need to change the client's code (GildedRose) or any of the other items types code.
Final solution
- Used
Templatepattern inDailyUpdaterhierarcy to support the generalDailyUpdatetemplate and allow each derived class (per item type) to implement their ownUpdateQualityalgorithm.- Why: instead of using a lot of if-else statements, every item type implement its own unique processing and the shared processing exist in the base Template class. When adding new item types, we can just add a new updater class without having to change the Template or any of the other updaters' code
- Used a
Simple Factoryto rturn the correct Updater for a given item (type).- Why: Move the knowledge about the different types of items away from the client code (GildedRose.cs) so it only relies on the abstract factory to return the correct updater for every item. When adding new item types, the client code will not have to change.
- Used
semi-Flyweightpattern to re-use the existing updaters instead of creating new ones over and over again. Note: In this instance, we could have used a dependency injection framework instead.- Why: this one in not critical. However it seemed wasteful to create new updaters over and over again when they don't have any unique (extrinsic) properties and can use just one updater of each type instead.
- Used a
Builderpattern to enforce building valid items.- Why: to prevent building items the violate the requirement. Specifically to enforce the quality limits and the legacy items special quality value.
Suggested Improvements if we could change anything
The used solution is good in general, I would probably do the following changes as well:
- Define
Qualityas a TinyType that protects its limits and deriveLegendaryQualityclass to support the legendary items special quality rules. - Add properties to Item:
- Type - use enum to define the type. This will allow better seperation between the name of the item and its type and will make the rest of our processing easier and more readable.
- Updater - a rference to the item's updater that can be created in the constructor depending on the item's type (usign the simple factory). From then on, this can be called directly on the item. This will use a
semi-Strategypattern in combination with theTemplatepattern in that the updater behaviour is being plugged into the objects.
- the
Buildermight be redundant now that the items can enforce their own valid creation. - Instead of using the
Flywieghtpattern, I would probably use a dependency injection framework to manage the creation of the updaters.