A little of refactoring goes a long way

Or yet another slightly misleading title for a blog post.

Context

In my product, I want to be able to propagate errors to the UI. Any kind of errors. Network errors, and all things related to network errors are a fascinating example.

Let’s say that I want to display a message every time a network request fails, or has an unexpected result (no data, unexpected response format, parsing errors, whatnot)

On way of doing that might be:

enum MessageType {
	case Error
	case Warning
	case Info
}

protocol DisplayableMessage: ErrorType {
	var message: String { get }
	var messageType: MessageType { get }
}

final class Message: DisplayableMessage {
	private struct Constants {
		static let networkErrorMessage = "Unable to load data from remote service"
	}
	
	private let _message: String
	private let _messageType: MessageType
	
	var message: String {
		return _message
	}
	
	var messageType: MessageType {
		return _messageType
	}
	
	init(message: String, messageType: MessageType) {
		_message = message
		_messageType = messageType
	}
}

Please note how DisplayableMessage, of course, extends ErrorType. Because being a good citizen in your platform matters.

I don’t want to bore the user to death with the details of what went wrong, so I just want to do this every time there is networking error:

let message = Message(message: "Unable to load data from remote service", messageType: .Error)

The little refactoring mentioned in the title

What’s the issue with the previous approach? Well, If I want to create instances of Message to model all network errors, I can assume I am going to have create said Messages in different points within my codebase (i.e. the parsing layer and the networking layer)

What should a sexy software engineer do? Embrace the Factory Method. How? Like this:

enum MessageType {
	case Error
	case Warning
	case Info
}

protocol DisplayableMessage: ErrorType {
	var message: String { get }
	var messageType: MessageType { get }
}

final class Message: DisplayableMessage {
	private struct Constants {
		static let networkErrorMessage = "Unable to load data from remote service"
	}
	
	private let _message: String
	private let _messageType: MessageType
	
	var message: String {
		return _message
	}
	
	var messageType: MessageType {
		return _messageType
	}
	
	init(message: String, messageType: MessageType) {
		_message = message
		_messageType = messageType
	}
	
	internal static func netWorkError() ->  Message {
		return Message(message: Constants.networkErrorMessage, messageType: .Error)
	}
}

 

Why does that refactoring go a long way?

Now we have a method with a name that clarifies the intent. I want a networking error, so I call the method that provides one of those. That way, clients of Message do not need to know anything about how that error is constructed (which parameters do I need to pass, in what order?)

Also, like any other creational pattern, this factory provides a layer of indirection when it comes to create the actual objects that we want to consume. Do I need my instance of Message to be unique? Easy, that’s an isolated change. Do I need to change the error message? Easy as well, that’s another isolated change.

Final words

Now, I guess you get the post title: a little of refactoring can go a long way. Sometimes.

Leave a Reply

Your email address will not be published. Required fields are marked *