Should Every Class Have an Interface?
This is part two in the sub-series of "Should Every Class Have an Interface?", and part of the bigger "What Makes Good Code?" series.
Other Peoples' Code
So in the last post, we made sure we could get an interface for every class we made. Okay, well that's all fine and dandy (I say half sarcastically). But you and I are smart programmers, so we like to re-use other peoples' code in our own projects. But wait just a second! It looks like Joe Shmoe didn't use interfaces in his API that he created! We refuse to pollute our beautiful interface-rich code with his! What can we do about it?
Wrap it.
That's right! If we add a little bit of code we can get all the benefits as the example we walked through originally. It's not going to completely fix "the problem", but I'll touch on that after. So, we all remember our good friend encapsulation, right?
Let's pretend that Joe Shmoe wrote some cool code that does string lookups from an Excel file. We want to use it in our code, but Joe didn't use the IStringLookup interface (because... it's in OUR code, not his) and he didn't even use ANY interfaces. The constructor for his class looks like:
[csharp]
public ExcelParser(string pathToExcelFile);
[/csharp]
On this class, there's two methods. One method allows us to find the column index for a certain heading, and the other method allows us to get a cell's value given a column and row index. The method calls looks like:
[csharp]
public int GetColumnIndex(string columnName);
public string GetCellValue(int columnIndex, int rowIndex);
[/csharp]
We can wrap that class by creating a wrapper class that meets our interface, like so:
[csharp]
public sealed class ExcelStringLookup { // ugh... we have to reference the class directly! private readonly ExcelParser _excelParser;
// ugh... we have to reference the class directly! public ExcelStringLookup(ExcelParser excelParser)
public string GetString(string name) { var columnIndex = _excelParser.GetColumnIndex(name); // assumes all of our strings will be under a column header var cellValue = _excelParser.GetCellValue(columnIndex, 1); return cellValue; } } [/csharp]
And now this will plug right into the rest of our code that we defined originally.
This doesn't totally eliminate "the problem" though (the problem being that some class doesn't have an interface (what this post is trying to answer)). There's still a class we're making use of that doesn't have an interface, but it looks like we've reduced the exposure of that problem to JUST this class and the spot that would construct this class. Are we okay with that?
Thoughts So Far...
Let's do a little recap on what we've seen so far:
- Having interfaces for our classes is a nice way to introduce a layer of abstraction
- Interfaces are just *one* tool to get layers of abstraction introduced
- If you wanted to have interfaces for all of the classes in your code and some third party didn't use interfaces, that code is likely not as common in your code base (especially if you wrap it like I mentioned above). This may not always be true in your code base, but it's likely the case.
- The amount of work to wrap things can vary greatly. Some things are straight forward to wrap, but you need to add many methods/properties. Sometimes it's the inverse and you only have a few things to wrap but they're not straight forward.
- The number of classes you'd need to wrap to get to this state can vary greatly... Since even built-in System classes aren't all backed with interfaces!
- There's certainly a trade off between the original work + maintenance to wrap a class in an interface versus the benefits it provides.
Watch this space for part 3 where we start to look at a counter-example!