Jimmy Bogard on coming to TDD and interface oriented Jesus and the aha moment of why we use interfaces:
An interface is a contract, that any implementation needs to adhere to. Consumers of the interface do not care about implementation details, nor can they, as the interface provides no other information besides the signatures it provides.
I quite like the point that Jimmy hints at. Classes, at a certain level, are tiny components. But is an interface really a contract? Yes... but it's not the whole contract; our tests and specifications are part of the contract of our components.
A component does a couple of things. It will a) tell its container environment what services it needs to run and b) what services it provides. It does this by requiring or publishing one or more contracts. As far as what we have right now in the mainstream, if we're pursuing the highest quality, it's up to us to deliver contracts that fully document not just the data (as interfaces do) but, at times, the behavior or semantic qualities of these tiny components we're writing. Sometimes those behaviors mean something to the supplier and the consumer.
Say we have a controller and that controller depends on a service. The controller creates a transaction and invokes a service method. Perhaps that service method then throws a business-meaningful exception. For argument let's say we have a shopping cart and you've just added -1 pairs of agile pants to your order. Wait a second... what just happened?! The fact the exception happened is surely a part of the contract! We have to deal with it after all and abort the transaction or take some compensating action. In the case of ordering some agile pants, maybe you want to display a message indicating that quantity should be one or more. Yes, I realize this is a bad example, but stuff like this necessarily happens in the real world.
What if -- just go with me here -- we have a dependency that can throw an exception? Is that not part of the contract? Last I checked there wasn't a way to say "hey, this interface can throw these kinds of exceptions" in C#. Consumers surely need to care about implementation details. What about invariants? If we simply have a structural contract we have nothing that expresses these behavioral elements. C# might get some spec# features, but we're not quite there yet. Preconditions, postconditions, and invariants are a part of the contract we take in.
When we're driving out interfaces and using a nice mock objects framework, we need to think about our consumers when we dive into a dependency and drive it out. What preconditions are you creating? What exceptions are you throwing? Do consumers of this service need to be aware or are they likely to want to handle these exceptions? If so, you need to return to the consumer and write some more tests. Get more mileage out of your code by applying defensive programming conservatively.
Simply put: contract = specifications + interface.
Interested in the potential benefits of DbC for more-robust contracts? Sifting through a quick Google search on the subject yields a nice post by Mario Gleichmann exploring the idea that interfaces all by themselves are poor contracts.
Posted
09-18-2008 9:44 PM
by
Dave Laribee