Refactor like a Pro: Adopt the Null Object Pattern with Small, Verified Steps

Click to play

April 27, 2021

0  comments

The Design Patterns book came out over 25 years ago. Are patterns still relevant today, especially for Swift developers?
At Quality Coding  we train iOS developers in technical agile practices—such as refactoring. Recently I refactored a fully tested view controller to MVVM, model-view-view model. The example stopped at an interesting place. Here’s the code:
There will be another video playing around with this code, this time exploring Model-View-Presenter. Subscribe to receive email notifications so you don’t miss it.

Code Review / Alternative Designs

This view model represents what to display about an item on sale—that is, it’s available at a discounted price. You can see it has one actual property, an optional Item. The remaining properties are all computed properties. In Swift, computed properties are syntactic sugar for functions with no arguments.
private struct ItemViewModel {
    var item: Item?
    var title: String? { item?.title }
    var image: UIImage? { item?.image }
    var currentPrice: String? { (item?.currentPrice).map { "€\($0)" } }
    var strikethroughPrice: NSAttributedString? {
        guard let item = item else { return nil }
        return isOnSale() ? AttributedText.strikethroughText("€\(item.listPrice)") : nil
    }
    var isStrikethroughPriceHidden: Bool { !isOnSale() }
    
    private func isOnSale() -> Bool {
        guard let item = item else { return false }
        return item.savingsRate > 0
    }
}
Every computed property uses the item with some kind of conditional. A lot of them use optional chaining. A couple of them use guard statements. The view model behaves one way if there is an item, and another if there is no item at all. We can model this sort of behavior with the Null Object pattern.
At the beginning of this series, all the logic was inside the view controller.
UML diagram: view controller with item
It was all unit tested, so we were able to extract a view model. Expressing this with a UML diagram, the view controller keeps a reference to the view model.
UML diagram: view model
This time, here’s what I want to do. Let’s change the view model into a protocol. This protocol will have two implementors: one for when an item is present, and the other for when it’s absent. The PresentItemViewModel will have an Item property, but it won’t be optional anymore. The NullItemViewModel won’t even have an Item property. The view controller can switch out its view model, replacing it with a different one. This will let us have separate code, where each implementor does less. And because the Item property will no longer be optional, this will make the code even simpler.
UML diagram: null object pattern
So our goal for this episode is to make this change. Afterward, I’ll share more about where the Null Object pattern lives in the context of patterns and refactoring. But as always, let’s not rip out the current code and rewrite it. We have unit tests, so let’s refactor, moving towards our goal in small, verified steps. Are you ready? Let’s go.

Moving the View Model into Its Own File

Refactoring starts in green (passing tests) and ends in green. So the first thing we want to do in any refactoring is to run our existing unit tests, to make sure they’re still green. During refactoring, we want to keep them green and stay out of red, as long as possible. By “red,” I mean either:
  • failing tests, or
  • broken code that doesn’t compile.

We want to minimize or eliminate the time we’re in red.

During refactoring, we want minimize or eliminate the time we're in red. "Red" means either failing tests or broken code that doesn't compile.

Code snippet sample

Improve your test writing “Flow.”

Sign up to get my test-oriented code snippets.

Click to Tweet
Now, we’re going to mess around with ItemViewModel here. The first thing I want to do is pull it into its own file. Unfortunately, there’s no automated support for this in AppCode, at least for Swift. So, we’re going to do it manually.
The view model can’t stay private if it’s going to move to a different file. So let’s take off the private and compile. This is to make sure that nothing else complains about another ItemViewModel elsewhere.
Then I create a new file, and cut and paste the struct. This gives us an error because it doesn’t know what UIImage is, so let’s import UIKit. And this should pass tests. Now we can play in here.

Adding an Explicit Initializer

We want to extract a protocol from this view model, but that’s hard to do in one step. Yes, that is the final goal. But we can get there while minimizing breaks by working in smaller steps.
Looking at where we call this view model, it’s created once and mutated in another place. Because this is a value object, when we mutate it, it gets swapped out for a different instance. Let’s make this explicit by creating new instances, either with or without an item.
So let’s add an explicit initializer. It doesn’t build, so we want to get out of code not building as fast as we can. When the app starts, the item is initially nil. Let’s confirm this by running tests.
Where it implicitly creates a new item view model, let’s make this explicit. Instead of changing the item, we are going to set it to a new ItemViewModel passing in the new item. We run tests to see if this passes, and it does. So this is a step forward.
I do encourage you to get the code and try it yourself using Xcode or AppCode or whatever you like.

Making a Factory Function

So now we instantiate ItemViewModels in two places. To create one thing or another thing we need a factory method. So instead of instantiating an ItemViewModel, we’re going to make a PresentItemViewModel or a NullItemViewModel.

We can start with a static function. It should take the same arguments as the initializer, and return an ItemViewModel. And at first, it’s simply going to return the same thing. This should compile.

Now one at a time, let’s replace the direct call to the initializer with the call to the static function. I run tests after each change.

The factory function gives us a way to do other things, to create one thing or another. So let’s declare those things. The easiest way to move forward would be to make a subclass, but you can’t subclass a struct. So as a temporary measure, let’s change this to a classThis happens often in refactoring: to reach your desired destination, you have to make a temporary change, then revisit it later to set it back.

Now that it’s a class, we can define new subclasses: PresentItemViewModel and NullItemViewModel. These are subclasses that do nothing other than existing. And inside the factory function, we’re going to make one or the other.

class PresentItemViewModel: ItemViewModel {}
class NullItemViewModel: ItemViewModel {}

Let’s do this in very small steps. I want to see if the item is nil and if so, unwrap it. So let’s add an if-else statement, where each clause is empty. This immediately breaks the build because now we need an explicit return statement.

if let item = item {
} else {
}
return ItemViewModel(item: item)

Now I can duplicate the return statement and put one into each side of the if-else. This is a provable refactoring called “Move line into all if/else branches” which doesn’t change any logic. I’m still making the same thing—one in the if and another in the else.

if let item = item {
    return ItemViewModel(item: item)
} else {
    return ItemViewModel(item: item)
}

With this conditional, we can replace one instance with the PresentItemViewModel subclass. Run tests. In the nil case, we can return a NullItemViewModel subclass, and run tests again.

if let item = item {
    return PresentItemViewModel(item: item)
} else {
    return NullItemViewModel(item: item)
}

Let’s pull this static function out of the class. When the view model becomes a protocol, we can’t have a static function inside of the protocol. Instead of just yanking it out and fixing up the places, we can do this in small, verified steps. Remember, we want to minimize the time where we can’t compile the code. So I start by copying the code, pasting it, and removing the static declaration. This should compile.

Then we want the static function to forward to the new standalone function. But we can’t use the same name, because that would be recursive. To break this cycle, let’s temporarily change the name of the standalone function to make2. Run tests.

Now we can go to each caller and switch them from the old function to the new function. I confirm each change by running tests. Once the old function is no longer called, we can get rid of it and run tests. Then we can rename the new function from make2 to makeItemViewModel. Always use your IDE’s “Rename” support. Once again, we confirm this change by running tests.

Duplicate Types Conforming to a Protocol

There’s no longer any need for the item property in ItemViewModel to be mutable. Let’s change it from a var to a let. While we’re at it, let’s see if we can make it private. We want to restrict information as much as possible.

Now let’s copy the entire thing and paste it into one of the subclasses. This gives us a few build errors. We can fix most by applying the suggestion that the methods should override their superclass. After that, one last bit is that the initializer needs to call super.init. With the compile problems out of the way, run tests to confirm that everything still works.

Then I copy everything and paste the same thing inside of the NullItemViewModel, with no changes. So now we have two copies of the original code, but inside of the subclasses.

It may be possible now to switch from subclassing to using a protocol. To do that, let’s define a protocol. I do this by copying a computed property, pasting, removing the body, and fixing up the declaration to be read-only—it’s get not set.

I can now declare that the base class ItemViewModel conforms to this new protocol. When I do, I get immediate feedback in AppCode that the title computed property is implementing a protocol definition, and that it’s overridden elsewhere. So that seems to be the way forward.

AppCode shows that a declaration overrides and implements

Let’s grab the other computed properties and do the same thing. Now we should have those little “implementor” indicators by everything except for the private function. Run tests to see that they still pass.

Now, with this protocol in place, we may be able to change the subclassing into a straight implementation of a protocol. But when I try, a bunch of stuff fails—all these overrides no longer make sense. I try removing them all in one shot, by doing a replace-all inside the selected area. And we don’t need super anymore because there is no superclass. And will this work? It doesn’t work. Why? Because the factory function is trying to return the old type, which no longer matches.

(All book links below are affiliate links. If you buy anything, I earn a commission, at no extra cost to you.)

At this point, the safest thing to do is revert, going back to green. I run tests again to confirm that we’re back in safe territory. Then we can make the preparatory changes we need to switch away from subclassing. This is another refactoring meta-pattern. It’s a small application of The Mikado Method.


We want this factory method to return an instance of the protocol, not the base class. Let’s see if this builds. It doesn’t, because the view model itself also needs to be the same type. So let’s fix that forward and see if this builds and passes tests. Now I can go back and try the subclass removal again. I change PresentItemViewModel from subclassing ItemViewModel to conforming to ItemViewModelProtocol. Then I get rid of the call to super and replace override-space, and run tests to see if this works. It does.

Now I can do the same with NullItemViewModel, copying and pasting the entire body to match. Now we have PresentItemViewModel and NullItemViewModel both implementing the same protocol. And they both have the same bodies.

A quick check in the IDE shows that nothing remains which calls the original ItemViewModel. I delete it and run tests. We distributed its body successfully into two new types. These types can now become structs again, instead of classes. And we can rename the protocol to ItemViewModel. (Remember, don’t rename things manually.)

Could you have done this in fewer steps by just ripping the code apart? Yes, but you would have been broken this whole time. Notice that even as I was doing these small steps, when I would run into a situation where things didn’t compile, I have a choice:

  • Fix forward, if I can do it in one step.
  • Or undo, and try making the required change first.

I’m trying to stay in green as much as possible.

Removing Unused Code to Simplify Each Type

Now we have a factory function that returns one of two implementors of a protocol. And both implementors have the same bodies. So we can start simplifying them.
From where we call NullItemViewModel, we know that item will be nil. So let’s see if we can just pass in nil. Tests confirm that this is fine. Then we can push this inward by setting the property to nil in the initializer. Now that the argument is unused, we can change the signature to eliminate it. In AppCode, I use the automated Change Signature refactoring.
AppCode "Change Signature" refactoring
Since this is a struct, we can now get rid of the initializer by setting the property’s default value to nil. Then we can use this knowledge that the property is nil to translate this code into a simpler version. Where there is optional chaining, the result is nil. I change one at a time, running tests after each change. The map call simply return nil, which we confirm with tests.
We can simplify guard clauses by taking the portion for the nil case. For isStrikethroughPriceHidden, it returns the opposite of isOnSale. We can simplify isOnSale to return false. Then by manually applying the Inline Variable refactoring, I fold this false value into the place that calls isOnSale. Then we can simplify it to return true.
And now we have a beautiful little NullItemViewModel that mostly returns nil, except one place where it returns true.
struct NullItemViewModel: ItemViewModel {
   var title: String? { nil }
   var image: UIImage? { nil }
   var currentPrice: String? { nil }
   var strikethroughPrice: NSAttributedString? { nil }
   var isStrikethroughPriceHidden: Bool { true }
}
What about in PresentItemViewModel? We know the value that’s going in is present, so it doesn’t have to be optional anymore. I use the Change Signature refactoring, and run tests to confirm the change.
Does this mean I can change the
item property to be non-optional? As soon as we do that, we have some things break. Let’s see if we can fix these. This is going to be the longest time I’m going to be in a broken state. Mostly, I apply the fix-it suggestions to remove optional chaining. For the map call, I can grab the subject and paste it over the closure argument. I eliminate the guard clauses. I’m careful to run tests after each of these changes.
Finally, the simplified code offers further simplifications, such as eliminating redundant return statements. I use AppCode to make those changes automatically.
Tests show that this is all fine.
And there we have it, we have two view models. We’re using the Null Object pattern to create one or the other. There’s a protocol that defines the interface that they have in common. We have the first view model, where the item is present. When this happens, we no longer have all those nasty conditionals and guards. And we have the null object version, which is super simple.
Isn’t this cool?

About Design Patterns

So there you have a demonstration of refactoring to the Null Object pattern. Let’s talk a little about the pattern and where it lives in the larger world of design patterns.
Design patterns are discoveries we make about ways to write code. We don’t want to use any particular pattern, everywhere. Instead, each pattern has a name, the context in which it’s useful, things to consider when applying it, and related patterns and alternatives.


If you have the original Design Patterns book, you may wonder if the Null Object pattern is in there. It’s not. The original book is not a complete catalog of patterns and was never intended to be. Instead, it launched an entire community of programmers, writing new patterns. Several books use pattern languages, and many patterns never made it into any books. I would like to see this community revived so that we can keep increasing our shared vocabulary of useful approaches.
In a way, the Null Object pattern is hidden in the original Design Patterns book. It’s a specific case of the Strategy pattern. Look again at the design diagram. We switch behavior by changing to a different implementation of an interface. Each of these is a different strategy. We could even have a third strategy to handle regular items separately from discounted sale items.
Strategy Pattern in UML
You can learn more about this pattern from the books about refactoring. Yes, books, plural. One of the entries in Joshua Kerievsky’s book Refactoring to Patterns is a refactoring named Introduce Null Object. The steps for this refactoring apply if your null logic is scattered across your code.


In the second edition of Martin Fowler’s book Refactoring, the Null Object pattern is mentioned inside of a refactoring named Introduce Special Case. As Fowler describes, Null Object is a special case of Special Case.


Here’s a fun fact. I haven’t used the Null Object pattern in nearly 15 years. That’s because it’s mostly unnecessary in Objective-C. Remember how at the start of this demo, we had computed properties that used optional chaining? In Objective-C, optional chaining comes for free, because you can send any message to the nil pointer. Whatever message you sent, it would answer zero. That zero had many meanings: false, or the number zero, or the first case of an enumeration, or another nil pointer. So in most cases, Objective-C gave us the Null Object pattern for free. Now we’re coding in Swift, where we want to avoid nullability. So consider the Null Object pattern. See if separating your code into two pieces, one for the null case and the other for the not-null case might give you simpler code.
And that’s as far as we’re going to take this exercise for model-view-view model, MVVM. In the next episode we’re going to start over but not do MVVM. Instead, we’re going to do MVP, model-view-presenter. Until then, happy refactoring!
Subscribe to receive email notifications of new posts so you don’t miss the next video introducing Model-View-Presenter.

WWDC21: What’s New in Unit Testing for Xcode 12.5

Jon Reid

About the author

Programming was fun when I was a kid. But working in Silicon Valley, I saw poor code lead to fear, with real human costs. Looking for ways to make my life better, I learned about Extreme Programming, including unit testing, test-driven development (TDD), and refactoring. Programming became fun again! I've now been doing TDD in Apple environments for 20 years. I'm committed to software crafting as a discipline, hoping we can all reach greater effectiveness and joy. Now a coach with Industrial Logic!

{"email":"Email address invalid","url":"Website address invalid","required":"Required field missing"}

Never miss a good story!

Want to make sure you get notified when I release my next article or video? Then sign up here to subscribe to my newsletter. Plus, you’ll get access to the test-oriented code snippets I use every day!

>