In “Refactoring, Improving the Design of Existing Code” by Martin Fowler, he presents the Move Method refactoring.
Here’s a video walkthrough of the Move Method refactoring in Swift and when you might want to use it:
Video Transcript:
Hey, what’s up everybody? It’s Andy from cleanswifter.com back to show you another refactoring from Martin Fowler’s book, “Refactoring, Improving the Design of Existing Code.” Each week I cover a different refactoring from his book showing you how to perform it in Swift 3. This week I’ll show you the refactoring called Move Method. Move Method is finally a refactoring that is the Mac Daddy of all refactorings. Move Method is very similar to ones we’ve seen before, like Extract Method. With Move Method, we’ll see how we can move code from one class to an entirely different class. When I think about refactoring, it’s not so much about changes within a single class. A lot of the refactorings we’ve looked at so far are like that- we’re massaging code within a single class. Right now, with Move Method, it might be the first time so far in this book where we’re going to see how you can take code from one class, and move it to another class all to improve the structure of your existing code. The reason behind Move Method is because one goal of software design is to keep coupling between “things” low. While you want to minimize coupling, you want to maximize cohesion. Cohesion represents how well defined a specific class’s purpose in life is. As soon as one class starts to do more than one thing, it is then not cohesive. For instance, you’ve heard of massive view controllers. Massive view controllers do not have high cohesion. And in fact, they are also probably very tightly coupled to other objects they interact with. Cohesion and coupling go hand in hand. Things that are tightly coupled to other objects often have very low cohesion. Where the opposite is also sometimes true as well. Things that are very cohesive, are also very loosely coupled to other objects. That’s the design you want to strive for, loose coupling and high cohesion. Move Method is a refactoring that is going to let you make changes to two different classes in order to achieve that design goal.
That’s the primary motivation behind Move Method. It’s when you’re going through some code and you see that the code you’re working with, and ultimately will be refactored, is interacting with another class on a detailed level. There might be several lines of code, all bundled together interacting with another class’s instance variables and methods. If you have a method on one class doing a lot of different things on another class, you want to consider moving that method to the other class. A common place I see this in my iOS code, particularly UI code, with UI widgets on screen and what I’m doing to them in view controllers. Let’s get more specific – UILabels
. Often times in our code you’re going to come up with a UILabel
which has one consistent style across your app. There might be a couple variations depending on where the label is appearing. Rather than repeating code to style your labels, what you can do, and what I’ll show you you can do, is create a subclass of that UILabel
and move a method from a view controller that might be styling that label into the UILabel
subclass. While I’ll show you this on UILabel
, it can apply to a bunch of other UIKit classes like UITextView
or UIButton
. Anything that appears on screen with a style where you might be writing 5-20 lines of code that would normally be in view controllers – move that to a subclass of the element that you’re styling. Let’s take a look at how to do it in Swift 3.
In this code snippet, there are three different things we’re going to be working with: a struct
which represents a name and has two strings for the first and last name. Next is a UIViewController
subclass. The third thing is this empty UILabel
subclass. To keep this example simple, I could have added some existing behavior into the label, or I could have omitted it and we could have created it during the refactoring. I didn’t do that, so right now it’s empty. Imagine either of those two scenarios, however you want. You’ll see how we’re going to use this with the refactoring. The interesting thing here in this refactoring is this setupNameLabel()
method. This is where we are going to apply the Move Method refactoring. The Move Method refactoring at its highest level says we’re going to move this method to a new place. Obviously, the first place I would think to move this to is the NameLabel
UILabel
subclass. What we can do here is create the new method in the new/destination class, and then, copy and paste the code. This is one of the only times it’s okay to copy and paste code. We can leave the empty shell of the method here for now. We’ll go back and fix that. Now we need to go line by line in NameLabel
and fix whatever syntax errors appear. The first one is indicating that firstNameLabel
can’t be found. Since we’re now in the nameLabel
subclass we don’t need to specify a label, since it is itself that should be modified. Since we’re in Swift, we don’t even need to specify self
. The method will set four things: the text color, the alignment, the background color, and its text based on our style for the app. Down here is another interesting thing that we need to change. It’s actually setting the text of the label based on the first name in the Name
struct
. This is interesting. Unfortunately, NameLabel
doesn’t actually know about Name
yet. We have a couple options here. How can we get the first name into this method? The first option is actually pass in the Name
itself, and this would compile. By doing this, we’re actually creating a coupling between NameLabel
and Name
– which may or may not be desirable. I’ll just point out another option where we can prevent that coupling is instead- send in the first name directly which we know to be a String
. That’s another option. I personally like this option because it’s a little more explicit doesn’t create that coupling between Name
and NameLabel
. Some would argue that this second option exposes too many details about what NameLabel
is actually doing because you need to know to pass the first name in as a String. Ultimately, these are two options you can think through for your specific use case.
Now we have the functionally equivalent setupNameLabel
method in the NameLabel
subclass. Now we need to go back to our ViewController
and make a few changes. First, we’re going to delete the old setupNameLabel
method. The compiler will now complain that the method no longer exists. Since we have the nameLabel
as a property on the view controller, we can call setupNameLabel
on that property. Since setupNameLabel
now requires a first name, I will pass it in as a parameter. And there we go, our refactoring Move Method has been applied.
There you have it, that’s the Move Method refactoring. We applied it in a way that we cleaned up our view controller. Anytime you can do that, in my opinion, is a win. It’s so easy as someone new to developing iOS apps, all the way to even senior engineers, to work with code and create code that results with hundreds of lines long view controllers. There have been pattern after pattern created to try to avoid doing that. I think that knowing the basics about refactorings like Move Method help you understand some fundamental ways that you can think about tearing those things apart without needing to commit to a whole new framework or pattern, and instead just write cleaner code. That’s the move method refactoring. Again, the reasons you want to do this are create objects with low coupling and high cohesion. Ultimately you’ll also think about better names for methods and better places for those methods to exist.
I hope you liked this video from cleanswifter.com. This was the Move Method refactoring from Martin Fowler’s book, “Refactoring, Improving the Design of Existing Code.” You can buy the book in the Amazon Affiliate link in the description for this video. Come back next week and I’ll have a new refactoring available.
Thanks, and happy cleaning.