First of all, thank you for interesting article.
One small question: As for the last principle DIP, Doesn’t your example show, that we need, following it, add an interface for every service and provide relation through it. If so, then how about KISS and YAGNI principles?
I mean isn’t it better to provide first “violated” example if we have only one type Program, and then if and only if we will have to add new types, provide an interface and incrementally refactor that code?
Otherwise we can stuck with Interfaces everywhere stuff.
Here are links on that theme: here and here.
Moreover there are languages, where is no such a type: interface. For example, as I know C++ does not have it, only abstract classes. So in that case, how DIP will be shown with your example above?
I mean isn’t it really better to rely on abstractions not in terms of language, but in terms of architecture?