If I combine A and B to make C, I have more than A and B. Now I have A and B and C and the relationship of A to C and B to C. Composition usually creates complexity. Interfaces (via aspects, aspects, traits, whatever) tend to simplify, when the complexity is quantitatively extensive enough to outweigh the cost of the interface complexity creation + the complexity saved by using the interface.
Why are you making this distinction between composition and interfaces? Just because you're using interfaces does not mean you aren't doing composition.
> Interfaces (via aspects, aspects, traits, whatever) tend to simplify, when the complexity is quantitatively extensive enough to outweigh the cost of the interface complexity creation + the complexity saved by using the interface.
That sentence is more complicated than anything that has been done with composition.
Inheritance is more limited in what you can do and implies a finite set of behavior states. With composition you have a system that can, in theory, have an untestable amount of permutations of behavior strung together.
Not really? You can use interfaces with composition that keep you from plugging anything into anything. What you're describing sounds more like functional programming while ignoring the names of functions.
For a simple example, I might have class A that contains an instance of interface/base type B and another field of interface/type C. If B and C each have 4 implementations that means class A can be composed 4 * 4 = 16 ways. But in practice there may only be matching types of B and C that line up such that there's really only going to be 4 distinct permutations of A. I could model this more easily with inheritance.
This has not been a practical problem in my experience. Because composition makes it easier to tell what something does, people are not putting the incorrect implementations of things together. They're essentially just functions with contracts. Very easy to read.
With inheritance, you can't know what something does until you know what everything it inherits does. The little method you override could change the entire execution flow of the code. It's extra steps with no benefits.
How?