Saturday, August 1, 2009

the beauty of interfaces

= my humble origins =

For my first few years as a programmer, I had a hard time understanding interfaces.

Before continuing, let me explain what I mean by "interfaces," since people use the word in several ways. I'm not talking about user interfaces (buttons and the like). I always understood those. I'm talking about formal interfaces in Actionscript (and similar structures in other languages), the kind you define like this:

public interface MyInterface {}

I understood how to define an interface. That's easy, as I showed above. And I understood how to use one:

public class MyClass implements MyInterface {}

What I didn't understand was the point of interfaces. So I just never used them, and my programs seemed to work. So I couldn't see what interfaces would add to the mix.

Every once in a while, I heard sayings like, "Program to an interface, not to an implementation," and I'd just shrug and move on. (Actually, that saying really confused me. I couldn't get past "Program to". What did it mean to "program to" something? I understood "program for," as in "program for money" and "program by," as in "program by starting with small methods first," but what on Earth did "program to" mean?)

When I read programming books, they rarely seemed to be written for me. Like most Actionscript developers, I had worked my way up to programming via design and animation. Most of my projects were small and didn't involve a team. But the books I read were constantly talking about large-scale applications and the need to clarify your code so that other team-members could understand it. "Don't name a variable 'a'," those books told me, because 'no one will understand what that means.'" That didn't really resonate with me, because as the lone programmer on small projects, I could easily remember what my variables meant, even if I gave them cryptic names.

What no one told me is that small-time programmers gradually morph into big-time programmers. Before I knew it, my bosses and clients were asking me to add many new features to my programs, and those programs started to grow larger and larger. I found myself writing dozens of lines of code, then hundreds, then thousands... This change happened so gradually that I didn't notice it until I was knee deep in variables named "a," "foo" and "a2".

By the sixth week of a project, I couldn't understand what I had coded on the first week. Worse, sometimes my clients would ask me to add features to a project I'd shelved a year ago. When I dusted it off and looked at the code, it seemed to be written in Swahili.

= I am a team of one =

One day it dawned on me that I DID work on a team! My team was comprised of me last week, me last year, me yesterday, me today and me two months from now. And I'd get really pissed off at the me from last week if he didn't leave the code-base tidy, so that the me from this week could easily navigate through it. Many coders do, in fact, work on teams of multiple people. Most of the time, I still work alone. But I've come to realize this doesn't matter. It's just as important for me to write clean code for myself as it is for someone at Microsoft or Adobe to write clean code for their co-workers.

= what is clean code? =


"Writing clean code" means at least two things:

1. Writing code that rarely has to be examined.
2. Writing code that, if it DOES have to be examined, is easy to understand.

Point one means that coding is a form of organized forgetting. If it takes me five hours to write a complex sort routine, I don't ever want to have to look at its guts again. So I make sure it works and then stuff it in a function called sort(). That name is the only part of the routine that the future me should need to worry about. As far as he's concerned, the function looks like this:


function sort( array : Array ) : Array
{
blah blah blah (it just works...)
}


But, because it's impossible to predict the future, there's a chance that the me of tomorrow might have to dig into the sort()'s guts. So I owe it to him to make the actual steps of the routine clear, too. That's the second point, above.

Sometimes I morbidly think of programming as if I'm making my will. I don't want my loved ones to be bothered by it while I'm alive. I just want them to know it exists. When I die, I want it to be easy to execute without anyone having to go through a lot of legal hassle. But if, for some reason, someone absolutely needs to examine it under a microscope, I want them to see that my will is well ordered, all i's dotted; all t's crossed.

= what is a programmer's job? =


Even when I first realized the import of clean code, I thought a programmer's job was to first make sure his program worked and only second to make sure it was easy to read. Over time, I realized that the opposite is true. My programs should first and foremost be easy for humans (including me next month) to read. Why? Because I'm inevitably going to spend more time debugging my programs than I spent writing them, and debugging is horrible (and costly in terms of man-hours) if the code is messy. Yes, it's important that a program works. But if the code is clean, it should be relatively easy to get it working if it's broken. But if the code is messy, it's hard to keep it from breaking and nearly impossible to fix it when it does break.

Have you ever had the horrible experience of fixing a bug and then finding that your fix caused another bug? And then fixing THAT bug and discovering that your second fix caused a worse bug? That's the horror of messy code. That doesn't happen with clean code.

= what does clean code look like? =

Clean code looks like anything else that's clean and organized:

"A place for everything and everything in its place."

This means making clear separations between parts: it means that you shouldn't write a class called GameAndHighScoreManager. You should write one class called GameManager and another called HighScoreManager; it means that you shouldn't write a function called moveCowboysAndIndians(); you should write one function called moveCowboys() and another called moveIndians().

And it means that you should separate interfaces from the implementations.

== what is an iterface? ==


An interface is the public parts of a class. It allows other classes to interact with that class. If there's a class called Human and a class called Dog, what parts of Dog does Human need to know about? (What parts of Dog do I have to think about while I'm programming Human? What parts of Dog can I forget about?) Human needs to feedDog(); he needs to walkDog(); he needs to know isDogAsleep(). However, he doesn't need to digestDogsFood() or makeDogsHeartBeat().

Class definitions give us an easy way to make some methods accessible while keeping others private, as I do here:


public class Dog
{
public function feed( food : Food ) : void { ... }

private function digest() : void { ... }

...
}


The problem with this is that I haven't separated my interface from its implementation. It's like a car that you have to drive by fiddling directly with the parts under the hood. Making some of those parts private is a good idea. But it's a bad idea to make other parts public by cutting strategic holes in the hood. Rather, a car should have an interface that's completely separate from its implementation. In fact, cars do. In a car, these interfaces are called dashboards. If you open a car's hood, you don't see little labels that say "public" and
private." Everything under the hood is private. Unless you're an expert, you should only be operating a car via its interface: the dashboard.

Alas, in the case of Dog, both interface and implementation are in the guts of the class. This can lead to some very confusing problems. For instance, let's say my Human class winds up feeding his dog:


public class Human
{
public function feedDog( dog : Dog ) : void
{
var kibbles : Food = new Food( "kibbles" );
dog.feed( kibbles );
}
}


Everything should work fine unless some idiot -- e.g. the future me -- starts screwing around inside the Dog class.

Well, actually there's a lot I can do inside that class without causing a problem. For instance, I can change...


public function feed( food : Food ) : void
{
yum yum yum...
}


to...


public function feed( food : Food ) : void
{
yuck yuck yuck...
}


Because Human doesn't care about the innards of feed(). All Human cares about is calling the method. Once called, it's free to like or dislike it's food, without affecting Human.

But what if I change food's parameters?


public function feed( foodType : String, calories : int ) : void
{
blah blah blah...
}


I've now broken Human:


public class Human
{
public function feedDog( dog : Dog ) : void
{
var kibbles : Food = new Food( "kibbles" ); //this no longer works
dog.feed( kibbles );
}
}


I could also break Human by renaming Dog's feed method to chew() or bite().

If you take your car in to be repaired, you probably won't be upset if the mechanic changes things around under the hood, as long as your car works when you get it back. You won't even know what changes he made. However, if he messes with the interface -- if he removes the steering wheel and sticks the break pedal in the glove compartment -- you'll be confused and angry. Similarly, Human has a right to be angry, because Dog has changed his interface. But I can't really blame Dog. After all, his interface is inside him. It's not really an interface, it's just part of his implementation that's been labeled public.

= making a formal interface =


So how do we give Dog an real, dashboard-like interface? In Actionscript, we do it like this:


package
{
public interface IDog
{
function feed( food : Food ) : void;
function walk() : void
}
}


By convention, interface names begin with a capital I. Inside the body of an interface, you list the signature of each public function (e.g. all the items on the dashboard). You don't label them "public," because everything in the interface is public -- that's the whole point of an interface. You don't list the body of the functions, because bodies are filled with implementation details. Implementation details do not belong on interfaces.

Now, I can tie my Dog class to this interface as follows:


public class Dog implements IDog { ... }


Anyone who wants to use my Dog class shouldn't bother looking at it's source code. Instead, they should look at its interface. It's interface contains everything users need to know. Dog itself is just a confusion of details. Users should...

... program to the interface, not the implementation!

By saying that Dog "implements IDog," that class is making a promise. It's promising to conform to the rules of IDog. It's saying, "Regardless of how I digest my food and whether or not I like it, you'll always be able to feed me via my feed method. I reserve the right to change how feed is implemented at any time, but I won't change the method name, the parameters it takes in, or what it returns. And, as a user, that's all you should care about."

= an interface is a promise =

I remember when I first read that an interface is "a kind of promise." I was confused. What happens if I break the promise? If it's just a promise, can't I just promise not to change method names or alter their kind and number of parameters? Sure, I can, but there's nothing to stop me if I break my promise. On the other hand, if I type in my code that Dog "implements IDog," I'll get a compile error if I don't keep my promise. For example, if I change Dog's feed method to this:


public function feed( foodType : String, calories : int ) : void
{
blah blah blah...
}


My code won't compile, because IDog says it should look like this:


public function feed( foodType : String, calories : int ) : void
{
function feed( food : Food ) : void;
}


And, in a good IDE (e.g. Flex Builder -- a.k.a. Flash Builder -- or FDT), I won't have to wait until compile time. Most IDEs tell you if you're not following an interface as you type your code.

So a real nuts-and-bolts reason to use interfaces is that they catch errors before the errors happen!

By the way, this doesn't mean that I can't change interfaces. I can. If I want to change the feed() method, I can go into IDog and redefine it. As with all aspects of code, interfaces evolve over time. The good news is that when you make changes to an interface, you get advanced warning about problems you might be causing. You can fix those problems before they rear their heads.

= advanced interface tricks =

Once you get the hang of interfaces, there are some useful advanced features worth using. For instance, interfaces can inherit from other interfaces:


public interface IMachine
{
function turnOn() : void;
function turnOff() : void;
}

public interface ITransporter extends IMachine
{
function beamUp( person : Person ) : void;
function beamDown( person: Person ) : void;
}


And classes can implement multiple interfaces:


public interface IKillable
{
function stabMe() : void;
function shootMe() : void;
}

public interface ITameable
{
function petMe() : void;
function feedMe() : void;
}

public class Shark extends Creature implements IKillable { ... }
public class Demon extends SupernaturalBeing implements ITamebale { ... }
public class Wolf extends Creature implents IKillable, ITameable { ... }


Note: it's another convention it name intefances I-something-able.

= interfaces can be used as types =

Coolest of all, you can type variables to interfaces instead of implementations. For instance "var dog : IDog" instead of "var dog : Dog". Here's a more complete example:


public interface IOpenable
{
function open() : void
}

public class Box implements IOpenable
{
public function open() : void { trace( "There's money inside!!!" ); }
}

public class Skull implements IOpenable
{
public function open() : void { trace( "There's a brain inside!!!" ); }
}

public class Door implements IOpenable
{
public function open() : void { trace( "Nothing to fear. Come on in!" ); }
}

public class DocumentClass
{
//notice how I typed the variable aThing
private function openAThing( aThing : IOpenable) : void
{
aThing.open();
}

//constructor
public function DocumentClass()
{
var box : Box = new Box();
var skull : Skull = new Skull();
var door : Door = new Door();

openAThing( box ); //There's money inside!!!
openAThing( skull ); //There's a brain inside!!!
openAThing( door ); //Nothing to fear. Come on in!
}
}


The magic here is that box, skull and door are three totally unrelated classes. They aren't children of the same base class. But they all share the same interface. So as long as you type a variable to IOpenable, that variable can store any of those classes.

= start your projects with interfaces =

Creating interfaces is a great way to start a project (you'll refine those interfaces as you go). If all you have at the beginning is interfaces, what else can you do except "program to interfaces"?

A great way to think about an Object Oriented system is as a bunch of things all talking to each other, pushing each other, pulling each other, etc. As such, each of these things can only interact with another thing through its interface. So you don't need to know anything about implementation details in order to describe the whole system. You just need to know the public face of each thing.

As an example, lets say that I'm making a Pacman game. I know nothing (yet) about how the objects will work, but I know that I'll need a maze, some dots (for the Pacman to eat), a Pacman and some ghosts to chase him and be chased by him. (To keep this example simple, I'll ignore other details, such as scores, lives and levels).

Opening my editor, I'll make a first draft at the interfaces:


public interface IPackman
{
function move( direction : String ) : void;
function eatDot() : void;
function eatGhost() : void;
function render() : void;
}

public interface IGhost
{
function move( direction : String ) : void;
function eatPackman() : void;
function avoidPackman() : void;
function returnToBase() : void;
function render() : void;
}

public interface IMaze
{
function render() : void;
}

public interface IDot
{
function render() : void;
}


== draft two ==


Looking over my initial draft, I notice that both Packmen and Ghosts can move and eat. And everything seems to be renderable, which makes sense in a visual system. So I redraft my interfaces to group similar methods together:


public interface IRenderable
{
function render() : void;
}

public interface IMovable
{
function move( direction : String ) : void;
}

public interface IEater
{
function eat( food : IEdibleObject ) : void;
}


When I later define my classes, they will all implement IRenderable. Packman and Ghost will both implement IMovable and IEater.

Notice the reference to IEdibleObject inside IEater's eat method. I need to define that, too:


public interface IEdibleObject
{

}


= empty interfaces =

Sometimes I create empty interfaces. At this point in development, all I know is that a bunch of different kinds of things -- packmen, ghosts and dots -- are all edible. I'm not sure if editable object will need to implement any common methods. And maybe they should all be descended from a base class instead of (or in addition to) implementing a common interface. But at this point, the empty interface serves to remind me that this is a sensible way to group certain objects. If I later decide to add a digestMe() method inside IEdibleObject, my IDE will remind me that I need to implement in this in all classes that are implementing it.

To wrap thing up (for now), I'll add the following definitions to my list of interfaces, though I expect I'll make more drafts when I start working on implementation:


public interface IEnemy
{
function returnToBase() : void;
function avoid( target : IGamepiece ) : void
}

//for dots, packmen, ghosts, etc. Do I need this AND IEdible?
public interface IGamepiece
{

}


= interfaces and class inheritance =

Sometimes I wind up using both interfaces and class inheritance. This happens when I want a whole family of classes to implement an interface in the same way:


//this class will never be instantiated as an object,
//which is why I have named it starting with the prefix "Abstract"
public class AbstractCreature implements IMoveable, IRenderable, IGamepiece
{
public static var STEP : int = 50;

//IRenderable forces me to include this method
public function render() : void
{
//override in subclasses
}

//IMovable forces me to include this method
public function move( direction : String ) : void
{
if ( direction == "up" ) this.y += STEP;
else if ( direction == "down" ) this.y -= STEP;
else if ( direction == "left" ) this.x -= STEP;
else if ( direction == "right" ) this.x += STEP;
//note: I'm not suggesting that this is how you'd
//animate a packman or ghost in real life.
}
}

public class Packman extends AbstractCreature
{
override public function render() : void
{
drawCircle();
cutPieSliceOutOfCircle();
animateMouthOpeningAndClosing();
}

private function drawCircle() { ... };
private function cutPieSliceOutOfCirce{ ... };
privare function animateMouthOpeningAndClosing{ ... };
}

public class Ghost extends AbstractCreature
{
override public function render() : void
{
drawBody();
drawEyes();
}

private function drawBody() { ... };
private function drawEyes() { ... };
}


I don't have to explicitly state that Packman and Ghost extend the interfaces IMovable and IRenderable, because they inherit that fact from their parent, AbstractCreature. All these classes also implement my empty (for now) IGamepiece interface -- just in case!

Note that the subclasses are free to implement additional interfaces that aren't implemented by AbstractCreature. For instance, if I have an interface called IPlayerCharacter (for the character played by the actual player of the game, as opposed to the AI ghosts), I could have my Packman subclass extend that.

= interfaces, me and Tim =

My relationship with interfaces has gone through three stages: at first I was baffled by them, then I got into them, and now I'm REALLY into them. For my third and current phase ("REALLY into them"), I have my friend Tim to thank. Tim, a seasoned programmer who has worked for Microsoft and programmed in many languages, makes interfaces for (almost) all his classes. Though I loved interfaces before I started talking to Tim about them, my immediate reaction to Tim's process was that he was taking a good thing too far. But the more I thought about it, the more I was unable to come up with an objection to Tim's way of working. If a class doesn't have a formal interface, then all its public methods are mashing up their interfaces with their implementations. That's messy and dangerous. So now I'm born again into the church of Tim.

Tim's idea is a bit revolutionary in Actionscript circles, but it's the norm in some other programming communities and languages. For instance, in Objective-C (a suddenly popular language, since one must use for iPhone development), all classes must have separate interface files. Unfortunately, since Actionscript doesn't force you to use interfaces, many of developers don't bother.

Not every one of my classes (or Tim's) implements an interface unique to itself. In many cases, large groups of classes all implement a single interface. Interfaces are, among other things, a great way of finding and expressing commonality. "I didn't think these classes had anything in common, but now I can see that they all render graphics. I'm going to make an interface called IRenderable and make them all implement it. Later, I can make a grand render-function that takes an array of IRenderable objects, loops through them and calls each of their render() methods!"

= when not to use interfaces =


Like Tim, I don't bother making interfaces for Value-Object classes (a.k.a Data Transfer Objects). If a class is just a collection of public properties...


public class ContactVO
{
public firstName : String;
public lastName : String;
public email : String;
pubic phone : String;
}


... then there's not much of point to making an interface for it. I also don't make interfaces for utility classes, which are generally collections of static methods. I don't really think of such classes as classes. In their cases, "class" is just a cheap and easy way to group together a library of helper functions.

But if something is an object that relates to other objects, it needs an interface. And since virtually everything in an OOP world is an object that relates to other objects, virtually everything in that world should have an interface.

No comments:

Post a Comment