As a developer, there's often a point when you're tasked to build something that's a key part of the architecture of your software. Maybe it's not a key component to all of the application, but it's pretty foundational at least for a part of the application. Often we put our thinking caps on, plan a bit of what we're about to code, and then dive right into it. If you do TDD, you might start coding your tests but regardless of your approach, you're likely going to start coding some classes pretty soon and forget completely about the use of interfaces.
You shouldn't.
Start With Interfaces
In my opinion, if you're writing code that's part of your application's foundation, you should start with interfaces. If you're already rolling your eyes and whining to yourself that now you're going to have to code some class and then have some interface that just redefines the methods and bloats your code, then take a deep breath. Here is my reasoning:
- The interface defines what goes in and out of your class. If you're only looking at this, you'll see how other developers have to interact with your class.
- If others can extend your work, you offer a lot more flexibility by providing an interface than forcing them to implement a concrete class.
- There's this fancy programming topic called Inversion of Control that you can make happen a lot easier just by starting with interfaces first, and you might need something like it down the road.
- Interfaces are excellent for your layered architectures.
It's not a big list so maybe you actually go through that on your one deep breath I told you to take. Maybe the tremendous code bloat of adding a few method signatures in a file has you so upset already that you can't even focus on what I just said. That's okay, you can always come back and try again once you've calmed down.
How Others Interact
The interface defines the exposed parts of what your class will implement. If you already have trouble making classes and trying to guess what to make public/internal/protected/private, this might help you out. Whatever you put in your interface must be visible to others outside of your class because interface implementations have public members. All the other functions you wanted to implement? You start to think "Well... maybe this would be useful outside of this class...". But now the question is, how useful? If it's that useful, then is it foundational enough to be part of the interface? If not, it should probably be scoped to the class and not outside of the class.
If you read my post on what makes a good API, I touch on a lot of the great points for interfaces. You probably still don't think this warrants the several-line code bloat. Hang in there.
Extending Your Work
So you have your whole API set up like a boss now. You think it's all fine and dandy because it gets the job done and passes all the tests. Awesome. But it's only awesome today. Tomorrow someone needs to extend your work. Someone wants to provide their own object into your code as a parameter now, let's say. Here's the signature that you wrote:
void CoolestFunctionEver(MyConcreteClass input);
You weren't wrong by being excited that everything works. You should be proud of that--congrats. But now if I want to provide my own input to your function, I have to extend your class like this:
public NicksConcreteClass : MyConcreteClass
{
// all the good stuff inside here
}
This may not seem that bad... but if I had another class that existed and already had most (if not all) of the information and functionality, I essentially have to duplicate it or create some sort of copy constructor to make it work. If you had done this with your original method signature and been thinking of interfaces:
void CoolestFunctionEver(IMyInterface input);
Then the world would be a better place. I could use my existing class, implement your interface, and just pop my reference right in there as a parameter. No need to add any duct tape or glue to make it work. It just works nicely.
If you don't see the value of this already, I would argue that you may not have written enough code for large projects. That's not meant to sound like a jerk or anything, but this is not an unusual scenario and unfortunately leads to more code bloat than defining an interface would have.
Invert That Control
If you haven't heard of this, it will seriously open your eyes to some better code design. Inversion of Control (IoC) lets others "inject" their own classes and dependencies into your existing code. This works really nicely when you have interfaces.
If your functions are operating on concrete classes, that means they depend on those concrete classes. Dependencies lead to coupling and code that isn't very extensible. And while yes, with an interface you're coupled to the API it's much better than coupling to an implementation. You may not have seen scenarios where this can come up, but let me try to provide an example.
Let's pretend we have a layered application with a presentation layer, an application layer, and a data layer. Suppose we have our entire application working and we have a MySQL-based model in our data layer. Everything is great. One day, someone comes along and says "MySQL has been working great for our customers, but in order to penetrate this other market segment, our users need to be able to use SQLite". (Okay, maybe this is a little contrived but still...) You think to yourself "No problem! We aren't using anything fancy from MySQL so implementing SQLite is going to be 5 minutes of work!". But then you realize it... Everywhere that accesses your data layer uses the MySQL model class that was created. Everywhere does it. You need to be able to use either though, so you can't even just replace it with all with the SQLite model you're about to create. Uh oh.
If interfaces were used from the beginning, this would have been a walk in the park. Literally, if you have one near the office, it would have been a 5 minute fix leaving you tons of time for a stroll. If all of the code referencing models instead referenced a nice clean model interface, say, IModel then you could "inject" your new model. In the few areas where you actually go to initialize your concrete model class, you could add the logic to do MySQL or SQLite and then everywhere else just sees it as an IModel. They actually have no idea what the underlying implementation does, and they don't care!
This point alone, in my opinion, is worth the "code bloat" of your interface definition. It could save you hours and the cost of a bottle of Advil.
Layers on Layers on Layers
This point kind of ties in with the IoC points and my points on extending your work. If you have a layered architecture, then you need to split up your code into pieces that are functionally different. Your presentation layer is responsible for rending things and making them pretty for user interaction. Your application layer does the heavy lifting, and your data layer does all that low-level stuff nobody wants to think about :)
Interfaces help to provide a nice layer of abstraction. If you declare your class that implements your interface in some other project or want to move its definition between layers, this won't really have any effect on the code relying on the interface so long as the interface definition doesn't move.
Why is this good? Well, to exaggerate my point, let's pretend someone is an absolute beauty and decides to implement your amazing data model in... your presentation layer. Great. Okay so despite it being in the wrong location, it works, and it works really well. If your data model interface resides in the correct spot and everyone is using the interface, then it's minimal work to move your data model class to the right spot. Cut it out, move it to the correct project/layer, and change the one/couple spot(s) that initializes the reference. All of that code that references the interface can go absolutely untouched. Talk about boss-mode refactoring. You just moved the bulk of your data layer between projects and across layers and didn't have to worry about breaking much code.
Summary
Leveraging interfaces in your code helps to provide a flexible and decoupled architecture. You can't really ask for much more than that. If you're concerned with adding a file here and there to contain some signatures because you think it's going to bloat your code base, then it might require learning the hard way when you need to pull things apart later.
This article is posted on code project technical blogs!