Hello everyone,
Recently I came across an article on Michael Feathers’ blog entitled Testing Yourself – Growing Through Refactoring. The article reminded me of a kata that I had put together a while ago that includes various code refactors that I regularly use to prime my brain before I get started on my daily duties as a software developer. After reading Michael’s article, I felt inspired to share this kata with the community.
“What is a kata?” you may ask. Well, in karate a kata is an exercise where one repeats a form many, many times, making improvements in each iteration. In software development, the term is applied in the same sense.
This particular kata helps me practice various code refactors, as well as a few useful shortcuts in my IDE (Integrated Developer Environment). The starting point is a simple project called EmployeeDemo written in Objective-C, with an Employee class and an associated unit test suite, which can be downloaded from GitHub.
The idea behind the kata is I start off with the Employee class that exposes a single method, payAmount, and then proceed to refactor the Employee class so that adding a new employee type (a Developer in this instance), will not violate the Open / Closed principle, and enable me to add any additional employee types later without opening up the Employee class for modification again. The Open / Closed Principle forms part of the SOLID engineering principles described in my previous blog.
The employee types are defined according to an enum called EmployeeTypes, and initially contain three types: Engineer, Salesman, and Manager. Each employee type returns its own pay amount as per the associated unit tests.
I will transform the Employee class from
To
Notice how the ugly switch statement has disappeared from the Employee class in the latter image? This is what will make it possible to add the Developer type without opening up the Employee class again
I came across the various refactorings in Martin Fowler’s excellent book, Refactoring: Improving the Design of Existing Code. The code samples in his book are written in Java, but I have adapted them to Objective-C for my code kata. A version of this kata written in Swift will be made available soon.
As mentioned earlier, I have made the kata available for download on GitHub. The repository has two branches: Master is the vanilla implementation of the Employee class and its test suite, with the tests asserting the employee pay amounts are as expected. The second branch, Refactor-Kata, shows each refactor as a separate commit, each with passing unit tests. You should be able to follow the refacotrings by navigating through the individual commits on the Refactor-Kata branch.
What follows is an overview of each step along the refactor process.
Self Encapsulate Field
The first step is to apply the Self Encapsulate Field refactor to the type field. I am accessing the field directly in the original version of the Employee class, but the coupling to the field will become awkward when I want to introduce a separate class to handle the employee types. Getting and Setting methods are added for the field, and only those are used to access the field
Replace Type Code with State/Strategy
Next the Employee class is refactored by applying the Replace Type Code with State/Strategy. The EmployeeType class has been added as a State class that will handle the various employee type codes
Yes, another ugly switch statement has been introduced here, but as I will show later, there will be only one at the end, and it is only used at creation.
To clean up the Employee class, I next use the Factory Pattern to extract the creation of the EmployeeType derivates from the Employee class to the EmployeeType state class
Then I extract the payAmount logic from the Employee class to the EmployeeType state class
Replace conditional with polymorphism
Now for the cool part – I get to use polymorphism This is the final refactor I have to do before adding the Developer type.
One by one, I comment out each leg of the switch statement in the payAmount method, which causes the tests to fail, until I override the payAmount method in the required EmployeeType derivative, and all that’s left in the payAmount method is a @throw statement. The payAmount method now uses polymorphic dispatch to determine the correct employee pay amount
Alright. Now I can do the Developer stuff. First, add the Developer type to the EmployeeTypes enum
Add the test for the Developer’s pay amount
Initially the new test won’t pass, but that’s fine – it’s called TDD (Test-Driven Development). We write a failing test first, then we write enough production code to make it pass.
To make the test pass, I need to add a new case to the EmployeeType’s factory method for the Developer type
And add the new Developer class as a EmployeeType derivative, and override the payAmount method
And I’m done! All the tests pass, and I have achieved my goal of making the Employee class open for extension, but closed for modification.
I find this kata very useful to show how one could use polymorphism to replace an if…else or switch statement (otherwise known as a conditional), and fix an Open/Closed Principle violation.