Skip to content

Program to an interface, not an implementation

Something happened today that made me go back to a long long time ago, in a continent far far away, when my mentor at the time said to me “program to an interface, not to an implementation”, and I found myself googling (actually, yahooing, it was that long long ago) what that could possibly mean.

It took me years to have a basic grasp of what he meant. And, as I mentioned, today I read some code that made recall that moment.

Let’s say that we are working on an app that needs to save some data locally. Let’s keep things simple and let’s say we need to save a username locally, to pre-populate a login form.

The simplest thing that could possibly work would be something like this:

But now, once we are saving and retrieving the user name, it would not be completely unexpected if we were asked to display that username someplace else. Let’s say, for example, why not, that in a splash screen

I guess by now you already see the problem.

We are duplicating code. And duplicating code is not good.

But, even if we were not duplicating code, there is another issue here, way more subtle in my opinion.

And the issue is that the code is Snippet 1 is highly procedural, and it is a clear example of programming to an implementation, and not to an interface.

Programming to an implementation

The code in sample 1 is highly imperative and extremely specific. It lacks any abstraction.

We are aware, and we express that clearly in the code, that the username is going to be stored in the user defaults. Not only that, we make the LoginScreen class aware of that fact, and what is worse, we make it also aware of how saving stuff to user defaults work, in painful detail.

This code was easy to write, but the fact that we are exposing the details of how the username is saved is going to be a guaranteed source of pain.

What would happen if we needed to save, for example, the user’s phone number as well? Or his score (if this was a game)? Or an authentication token? And what if we also need to display any of that data in more that one place? We would scatter our source code with extremely specific references to the way we read/write that data.

Programming to an interface

The way I see it, programming to an interface is avoiding implementation details to be known by anyone that should not be aware of said details. And the way to achieve that, is by making my code as declarative as possible.

So, what do we have here? We have data that can be used to identify a User, and we need behaviour to make that data persist between app sessions.

Model objects

So, the first thing to do is identify the Model. In this example, the model is not a username or a telephone number, or a token, it is the abstraction that groups all of those together: a User.

So, let’s declare a User:

Abstract behaviours

Now, what do we need to do with that User? Persist it. Do we need to know how to persist it? No, we don’t. Do we need to know if we save it to UserDefaults, to a remote server, or to the Keychain? No, we don’t.

And this is the whole point of this post: more often than not, we don’t need to know how something is done, we just need to know that something is going to get done. In this case, we don’t need to know how the user’s data is going to be saved, all we need is to know that someone is going to take care of having that data available for us when we need it.

That way, we shift from writing step by step descriptions of what to do, to write high-level expectations of how things should collaborate with each other. We shift from imperative, to declarative code.

So, what’s the behaviour here? If we describe what we need in the most possible abstract way, we need User data to be available.

Abstract stuff, in Swift, like in any other OO language, is the domain of interfaces. In Swift, though, we call the Protocols.

And protocols need an implementation:

Now, the original code would look like this:

Which is better, if you only look at the save() and read() methods.

In fact, you might be wondering if we are actually in a better position now. We have written a protocol, to abstract things, but after all, in LoginScreen we create a concrete class to implement it. And you would be right, if it wasn’t for the coming step.

If we pass LoginScreen the actual implementation of the DataStore protocol that we want it to use, LoginScreen wouldn’t actually know who or how is saving and fetching data. That is called “inverting the dependency”. And I have written about it a couple times before.

Now we are getting somewhere. We have one abstraction (DataStore) and we have a class (LoginScreen) that is provided with a concrete, and unknown to it, implementation of that abstraction. We have shifted from something extremely specific and imperative (LoginScreen saving and reading to user defaults) to something abstract and declarative (LoginScreen asks DataStore to save or fetch data, not caring about how it does it).

And, to me, that is what “program to an interface, not an implementation” means.

Be First to Comment

Leave a Reply

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