Mikado kata
Hands on Mikado method to refactoring
Facilitation notes
This kata is an abstract of the one available here in java and C#.
Before we start
Clone the java code from here
Connection
What was your last refactoring ?
How was it ?
What was your goal / plan ?
Concepts : Mikado method explained
Mikado methodConcrete practice
Open existing code
Draw class diagram
What does the code smell ?
Does it break any SOLID principle ?
Baby steps
Together :
Create the project
Create the test
Then ask question :
What is the next step ?
Do it : extract common dependencies
Naive way : just take the UI
Roll back
Update the graph
Conclusion
What do you think about this method ?
How can it be useful ?
Use it on your next refactoring
What is your feedback ?
Context
Pasta Software is a small family-owned company. By good luck and coincidence, their pride and joy MasterCrüpt (TM), has been sold to a new customer.
But now, problems arise.
The very, very secret obfuscation algorithm in the application cannot be exposed between the old customer Gargantua Inc and the new customer, Stranger Eons Ltd.
Without really knowing it, Pasta Software has just put themselves into technical debt by changing their business model, i.e getting a customer with slightly different needs. They did not see this coming and the code isn’t flexible enough to offer an easy way out.
The design has to change so that confidential information doesn’t leak between their customers.
It is virtually impossible to tell how a system will evolve in advance. Trying to anticipate all future changes to a system and make the code flexible enough to handle them only makes the codebase bloated. In fact, that kind of over-design makes the code more complex and will likely become a problem in itself.
Ubiquitous language
Leetspeak : the way hackers and crackers avoided text filters on Bulletin Board Systems (BBS) in the eighties by re-placing alphabetic characters with non-alphabetic, but resembling, characters.
For instance, in one leet dialect ’leet’ is translated to ’l33t’, where ’3’ is the mirrored capital ’E’.
Existing code
Here is the entire code of the system :
Baby steps
We will run this kata in baby steps.
The code is really easy and could be rewritten in only a few minutes but the whole exercise has been created to practice Mikado method and to do so the creator proposes to follow those steps :
Draw the class diagram of the system
How does it work ?
The UI class creates an Application instance.
The Application instance is called with the string to leet and a reference to this UI class.
The Application instance call back UI to set the leeted String value.
The Application class uses the Leeter class, to leet the given string and calls back to the provided UI instance with the leeted value.
The Application class also contains the main method for the application.
The Leeter simply performs the leeting of the incoming message by substituting the character ’e’ with the character ’3’.
It only takes a few classes to create a mess. For instance there’s a circular dependency between UI and Application, not to mention the mysterious callbacks.
In reality, codebases are a lot bigger and more complex by nature, so the ways of creating a mess are almost unlimited.
Mikado Method can be applied no matter how big or small the codebase is. The method serves as a guide and helps to identify the critical change path in order to be able to deliver.
Before touching the code
Before making any changes to the code, we make sure that everything works. We check that :
The code compiles
All the existing tests run
There are no checked out files nor uncommitted changes
We start from a clean state.
Define our goal
Now, our goal is to create a system that can be delivered to Stranger Eons Ltd.
Mikado Goal : new deliverable for Stranger Eons Ltd
Find out what we need to do
We analyze the situation to find out what we need to do.
We seek things to try, where actual consequences of our changes tell us what steps we need to take next. Hence, we strive to make changes that will give us that feedback, as soon as possible.
In the Java world, "New deliverables" means we will probably have a separate project source root from which we then create a JAR-file for Stranger Eons Ltd.
Step : "Create Stranger Eons project" as a prerequisite to our business goal, the root of the Mikado Graph.
In the naïve spirit, we just create the new project to see what happens. So far so good.
We check in to the main development branch if everything works as it should and the changes make sense : no sense here.
What next?
Once again, we want to seek things to try.
Driving development using Acceptance Tests is something we want to do
Tests often give us real feedback about the next step
We choose to create a test for Stranger Eons Ltd in the new project, a test which will look much like the one we have already for Gargantua Inc.
Step : Add a tests case for Stranger Eons
Can not compile : the UI class is in the mastercrupt project and we must avoid a dependency to that project, since it still contains code that can’t be shared
To resolve the compilation problems, one option is to duplicate the entire project. We won’t do that because we think duplication is bad, as described in detail in Don’t Repeat Yourself - DRY.
So, we need to change the chain of dependencies in order to allow us to add the test case to the project without any compilation errors.
Extract common dependencies
What to do about the dependency problems ?
UI has some common logic which we want to use in both projects
Choose to create a new project for the UI code and this project will be used as a common dependency :
mastercrupt.shared
for example
We can't compile because of the circular dependency
Rollback time
Now it’s time for a non-intuitive step, but an important part of the method: Back out broken code.
Step : Roll back to the tag "Before new client".
The strangereons
project has compilation problems and we don’t want to do anything there, nor any place else. So, we roll back to the very beginning.
We update our Mikado graph
Decision : Break dependency between UI and Application.
Break circular dependency
We often add decisions, like breaking dependencies, to the Mikado Graph even before we know exactly how to resolve them. Such items serve as a decision node, and they help us defer commitment until the last responsible moment.
Dependency Inversion Principle
A common way to break circular dependencies is to introduce an interface for one of the classes involved in order to change the direction of the dependency.
We choose to introduce an interface for the Application
class, the ApplicationInterface
.
Step : Extract ApplicationInterface, including method leet(...)
Step : Inject ApplicationInterface instance into the UI constructor
By doing this we have solved the circular dependency problem
Commit : Broke circular dependency between UI and Application
We can now tick the 3 leafs : (here in green on the graph)
Extract Application interface
Inject instance of ApplicationInterface
Break circular dependency
Move the code to new project
We perfectly now what are our next steps : go through our graph to our goal
Create UI project
Move UI code to UI project
Commit : UI-code in separate project
Create the Stranger Eons project
We have reached our goal and can stop here.
Conclusion
We’ve managed to morph the code from one state that didn’t allow us to do what we wanted, to one that actually does what we want.
We did this by using the Mikado Method to
Write down the goals
Seek things to try
Back out broken code
Fix the leaves first
What do you think about this method ?
How can it be useful ?
Use it on your next refactoring
What is your feedback ?
To go further
Last updated