Over the weekend I mentioned the Goldilocks approach to business, software engineering, and just about everything else: balance.
When writing frameworks - or any software which might be used by a third-party developer - you need to find the right balance between extensibility and abstraction. Avoid guessing too much - don't make any, or at least many, assumptions. On the other hand, if it's too generic it doesn't really add a lot of value either.
The balance can be reached by making operations generic and encapsulate concrete implementation. You are trying to find cohesion between objects, and building this up around classes, interfaces and delegates. The model I've really followed for the last couple of projects is heavy on interfaces. The idea is that everything is pluggable on both sides. What you're ultimately concerned with is an operation on an interface.
Each concrete implementation is a completely self-contained object that tells others what it needs to know, but at the same time doesn't assume what operations will be called on it. It is self-describing, but that's about it.
I equate this to "asking the locals". When you're in a foreign city, you shouldn't try to guess what the best place to eat is; you should ask those who live in the city and can make a better guess. At the same time, different people can visit the city and be interested in different types of food.