As JeffH was saying, there's definitely a place for inheritance and his example of member data and specific member methods still holds. Interfaces are basically a 'contract' of what an object can do or provide (such as the foreach example), what this means is that you can 'tack' on an interface and pass ANY object type that inherits that interface to a consumer of that api. This has a lot more power than polymorphism.
An interface is like a 'shim' or adapter if you will and that's why you won't see them used so much in sample code as they only confuse the issue.
@John,
I don't have anything that explains the concept at the moment, but I'll try a brief(?) description of the process.
In TDD you create your tests first for class that you want to test, you create these test methods to test your class methods and you will find that some of these methods require other objects.
To inject these dependencies that don't exist yet (you write your tests first remember) you need to create something, here's where you would use an interface.
You would then write a 'helper' or mock class that consumes your new interface and all this class does is help make the tests pass, even with BS code/data, just make it pass. You have now 'inverted' your dependency by letting the interface worry about it instead of your new class being tightly coupled to an actual object.
That sounds counter intuitive at first but you are not testing your dependencies just yet, only that the implementation details of the class you are working on behaves as expected, integration testing is when you plug it all together to test real dependencies all work together.
So, you have tested your new class and you have one or more new interfaces (apis if you will) for your dependencies, you can now create tests for the real dependency classes instead of helpers inheriting the interfaces as the api. Rinse and repeat.
This way you don't end up writing code you don't need as you are designing the api as you go and re-factoring out unused code and keeping it all DRY. It also allows you to change the implementation details without breaking anything, you could even plug in completely different dependencies as long as you implement the interface api.
A good example of this is something like the ActiveRecords or PDO database api's, they have a simple connection method that is used to connect to any db by using an interface to abstract the actual connection details away. You could do this with inheritance but that would means all the different companies would need to agree on the abstract base class and methods they would all need to use. By using an interface you can change the underlying database connection details without breaking all of the legacy code that uses the connection api. This is a good separation of concerns if you will and TDD pretty much enforces you to code this way.
So now you're thinking "that's more than double the code I really need to write my app" and this may be true at the start but remember some important points:
- you only write code you need, you don't go off writing stuff you think you may need but never use
- you change only the implementation details, not the api
- you have a much better and maintainable architecture, the tests serve as excellent documentation as well
- you run your tests continually throughout your dev process catching bugs as you go
- the tests provide an exact location of a problem when a test fails, setting break points and stepping through code is seldom done.
- all of your testing is automated saving you many more hours debugging than it took to write the tests (this is key!)
- you will have the confidence to change things any time you like as any bugs are quickly found and extra new tests may need to be written and the problem easily fixed.
It's a big topic but well worth the effort I think.