I guess that this question raises many issues. I'll try to keep things as short as possible:
I could give over lets say integers that represent Ids that do simply
not exist.
From my point of view, programs are (computational) models that represent your problem domain (an analogy can be made with physicist or astronomers writing equations to represent a phenomenon). When you model with objects, what you are doing is creating a representation of that domain using some particular rules. So, going back to your question, you could represent what is conceptually an ID with an integer, but then you will have a concept in your problem domain that is not properly represented (because, for example, there are integers that are not valid IDs). Also, besides the conceptual issue, the problem is that you can't add (and thus delegate) new behavior to an integer and if you could (e.g. in Smalltalk everything is an object and you can extend any class) it would be also wrong from the modeling point of view. As a general rule of thumb I consider a model lacking an abstraction when I have to write a behavior in an object that shouldn't have a given responsibility. In this case would be something like having a Util
class with a class method isValidId
.
If I would use an object I actually know that there is valid data for
it otherwise the object would not have been created (exception) or
would be in an invalid state which I have to check for.
Agree 100%. I've written a couple of articles about this you may find useful (disclaimer: I work at Quanbit Research)
This article says that it is evil to use concrete objects for this,
instead I should hand over their interfaces (as you all know, I
guess). Changes to the concrete type (not the interface) would cause
the dependent type to "breakdown". Is that so in all cases?
The story involving objects, types and interfaces is quite long. To sum up a bit, ideally you should program against interfaces and not concrete classes, since (in theory) you should only care that a given object (e.g. a parameter) implements a set of messages with a predefined semantic. However, if you go this road, in practice you will see that one class generally implements more than one interface and the bookkeeping of having all the interfaces in sync with the classes is prohibitive. I usually work with dynamic-typed languages, so this is not an issue in most cases for me, but if I had to work on a statically typed language, I would use interfaces when the system has to interface with code form outside the project or in APIs between modules. In other words, I would try to lower the coupling in "the boundaries" of the system.
Is this also true for a closed single project environment? I would
only understand that, if interfaces are - once written - untouchable
and never be modified/refactored again.
I have to disagree here. A program, being a computational model, reflects what we know at a given point in time of our problem domain. As such, the more we work on it, the more we know about it. Programming involves learning, and as we learn we better understand things; thus our models change. And as our models change, the elements that we use to represent them also change (like classes or interfaces). As time goes by you will see that your model becomes more robust and conceptual changes will slow down and at some point you will have a stable one. But changes are rafactorings are things that you should expect :).
HTH