Day 9 of the Blogging A-to-Z challenge brings up one of the key concepts in object-oriented design: the interface.
In object-oriented design, rule #1 is "program to interfaces, not to implementation." In other words, when interacting with an object in your system, you should care about what behaviors and data you need to use, not what the object actually does with them.
Going back to last week's room-and-window example: the original problem was that I want to close all the windows in the house with one method call. The solution on Saturday involved having a Room
class that exposed a list of Window
objects which you could iterate over and call the Close()
method on each.
That's great, but what if I want to close all the windows, doors, fireplaces, and anything else that I can close, because of the zombie apocalypse? In Saturday's implementation, I need to know that rooms have windows, doors, fireplaces, and other things specifically. That is, I need to know how each room is implemented.
I really don't care whether the thing is a window, a door, or a squirrel's mouth; I want it closed now, before the zombies get in.
So let's do this instead: define the behavior of a thing that I can close, and operate on the behavior rather than the thing itself. In C#, I can define an interface, which would look like this (and refer back to Saturday or the code sample file for the rest of the implementation):
namespace InnerDrive.DailyParkerAtoZ.WeekOne
{
public interface ICloseable
{
void Close();
}
}
That defines a single behavior that a class can do. (Interfaces can also define data and events, but that's beyond the scope of this post.)
Now I can add the interface to the Window
class:
public class Window : ICloseable
{
public void Close() { }
public void Open() { }
}
And then refactor the Room
class so that you can add and close all manner of closeable things:
public class Room
{
public void Add(ICloseable closeable)
{
_closeableThings.Add(closeable);
}
public void CloseEverything()
{
foreach (var thing in _closeableThings)
{
thing.Close();
}
}
public IEnumerable<ICloseable> Windows =>
new List<ICloseable>(_closeableThings.Where(p => p is Window));
private readonly List<ICloseable> _closeableThings = new List<ICloseable>();
}
Well, that looks different. Instead of adding windows to a room, I can now add anything that can be closed. And because of that, at lines 16 and 17, in order to make sure the Windows
list still works, I have to change the code so it only returns windows.
It's still a dumb piece of code, but you can start to see how powerful interfaces are. If in the future I create some new class with a Close()
method, I can apply the ICloseable
interface to it and stick it in a room. (Books, maybe? Escrow accounts? People named Glenn?)
There are many, many implications to all of this. But this is one of the foundations of OO design. I don't care what the thing is, I care what it does or knows. Program to interfaces, not to implementations.