An OO program cannot be adequately described by its code
alone. It is not just code with an entry-point. Instead, it is
an object graph with an entry-point. We can define an
OO program more accurately, if we have the means to define both
the code and the object graph. IoC containers provide the
programmer with a handy facility to assemble the object graph
needed by a program.
The difference that OO brought to mainstream programming, is
the ability to achieve variability through the use of
polymorphism. Every time we introduce a new class in a program,
encapsulating a chunk of the overall logic, we create a new
variation point. This variation point can be exploited by
varying the implementation of the class which demands code
changes that can be extensive. IoC containers bring the
variation points to the surface, where objects can be defined in
a single place regardless of their clients.
A lot of IoC containers use code as the configuration
language to define objects. My belief is that IoC containers
that use declarative configuration (mostly XML) offer an
additional advantage, that of being more susceptible to
introspection and editing, even by non-programmers. Treatments
like those Lamino does would not be possible on code-based IoC
configurations.