In “Refactoring, Improving the Design of Existing Code” by Martin Fowler, he presents the Remove Assignment To Parameter refactoring.
Here’s a video walkthrough of the Remove Assignment To Parameter refactoring in Swift and when you might want to use it:
Hey what’s up everybody, it’s Andy from cleanswifter.com and I’m here to show you another video in my series covering Martin Fowler’s book, “Refactoring, Improving the Design of Existing Code.” Each week I post a different video that shows you an example of the refactoring in Swift 3 and this week we’re going to look at a refactoring called “Remove Assignment to Parameter.” This is an interesting refactoring because technically the smell that this refactoring fixes isn’t even in Swift 3. I was debating on not even creating a video for the refactoring, but what’s interesting, is that the change from Swift 2 to Swift 3 is actually what prevents this smell from happening. Long story short, if you can continue writing your Swift 2 code, it is possible to run into the smell that this refactoring fixes. But in Swift 3, it’s actually preventable by the compiler. The other thing that I wanted to bring up is that because of the reason that Martin Fowler actually presents this refactoring. It’s primarily intended to resolve issues of clarity when looking at methods wondering whether parameters are being passed in by value or by reference. I’m going to clear that up for how Swift works and show you how the parameters are handled, and again it varies from Swift 2 to Swift 3.
The overview of this refactoring is essentially that if you have a method where you’re passing parameters to that method and you see code that then assigns new values to those parameters, that’s where you should introduce a temporary variable to use instead of assigning new values to the parameters. Essentially, parameters when passed to a method, should be treated like they are final. There shouldn’t ever be anything modifying the values of those parameters. You can of course call mutating behavior on the objects themselves, but don’t ever change the assignment of that object. That can be true for reference types or value types, and both have the same rules in Swift 3. Let’s take a look at this refactoring.
Here we have a class that is a name formatter. Right now all it has is a function that formats a name provided two strings representing the first and last name. You can see in the documentation that the method is intended to provide the format of “last name, first name”. Right off the bat, this is the smelly version of this code where
lastName is passed in as a parameter and then immediately assigned to with a new value. Essentially, what this is doing is modifying the value of last name by appending a comma with a space and then returning that last name with first name appended. If you’re coming from Swift 2, you might have seen code that looks like this. This is telling the compiler that
lastName can be changed- it’s not a constant in the scope of this method. Now you can see in Swift 3, which is running in this playground, that the compiler won’t even allow me to do this. For instance, if you end up Googling how to make a variable writable, you might see StackOverflow answers indicating to use the
var modifier in the parameter definition. This works in Swift 2. It does not work in Swift 3. If you do put this back here, the compiler will actually tell you that parameters may not have the
var specifier, and it even gives you a quick fix replacement in which a temporary variable for
lastName is created and then used just as we had it in the prior implementation. That in fact is the refactoring, “Remove Assignment to Parameter.”
Now you might be telling yourself, “well Strings, they’re classes so they’re going to be reference types in Swift. What about value types?” Well let’s create a
struct and see if the behavior is any different because normally I’d think, “Well I’m passing a value type in here, I have no guarantees that thing is going to be final.” I would suspect that it could be changed at any time. Instead, let’s create a
struct representing a name. Name has a first name, and a last name. Let’s create a new version of this same formatter method
fullName which takes a
Name from our
struct. Similarly it returns a
String. To make the compiler happy at this point we’re going to return an empty
String. The first thing we should try here is to assign a new value to the name parameter. We can’t as well because by default, whether it is a
struct or a
class, value type or reference type, method parameters in Swift 3 are final, and only final. That is a new Swift 3 feature. Similarly, the fix here would be to create a new temporary variable and then continue on using that temporary variable.
There you have it, that’s the “Remove Assignment to Parameter” refactoring in Swift 3. Now remember, technically in Swift 3 you’ll never find yourself in a spot where you have to actually perform that refactoring because the compiler won’t let you get there. Method parameters in Swift 3 are final by always and ever. This is unlike Swift 2 where you can use the
var specifier to make the parameter mutable. Remember, if you’re Googling and find a StackOverflow answer or other old dated material that tells you to use a
var specifier in order to add mutability to parameters, don’t believe it and realize that is a Swift 2 convention that is no longer possible in Swift 3. Feel good that if you’re writing Swift 3 and beyond, you won’t ever run into this. And if you do run into it, remember why. It goes back to the clarify of local reasoning. You won’t ever need to remember whether that parameter was passed by value or reference. Changing parameters passed by reference is really bad because you don’t know what all could be affected.
I hope you enjoyed this video from cleanswifter.com’s video series on refactoring covering Martin Fowler’s book, “Refactoring, Improving the Design of Existing Code.” You can buy that book in the description of this video using my Amazon Affiliate link. I’ll see you next week. Happy cleaning.