Lets look at this practically

Obj 3
now knows Obj 4
exists. So what? Why do we care?
DIP says
"High-level modules should not depend on low-level modules. Both should depend on abstractions."
OK but, aren't all objects abstractions?
DIP also says
"Abstractions should not depend on details. Details should depend on abstractions."
OK but, if my object is properly encapsulated doesn't that hide any details?
Some people like to blindly insist that every object needs a keyword interface. I'm not one of them. I do like to blindly insist that if you're not going to use them now you need a plan to deal with needing something like them later.
If your code is fully refactor-able on every release you can just extract interfaces later if you need them. If you have published code that you don't want to recompile and find yourself wishing you were talking through an interface you'll need a plan.
Obj 3
knows Obj 4
exists. But does Obj 3
know if Obj 4
is concrete?
This right here is why it's so nice to NOT spread new
everywhere. If Obj 3
doesn't know if Obj 4
is concrete, likely because it didn't create it, then if you snuck in later and turned Obj 4
into an abstract class Obj 3
wouldn't care.
If you can do that then Obj 4
has been fully abstract all along. The only thing making an interface between them from the start gets you is the assurance that someone wont accidentally add code that gives away that Obj 4
is concrete right now. Protected constructors can mitigate that risk but that leads to another question:
Are Obj 3 and Obj 4 in the same package?
Objects are often grouped in some way (package, namespace, etc). When grouped wisely change more likely impacts within a group rather than across groups.
I like to group by feature. If Obj 3
and Obj 4
are in the same group and layer it's very unlikely that you'll have published one and not want to refactor it while needing to change only the other one. That means these objects are less likely to benefit from having an abstraction put between them before it has a clear need.
If you are crossing a group boundary though it's really a good idea to let objects on either side vary independently.
It should be that simple but unfortunately both Java and C# have made unfortunate choices that complicate this.
In C# it's tradition to name every keyword interface with an I
prefix. That forces clients to KNOW they are talking to a keyword interface. That messes with the refactoring plan.
In Java it's tradition to use a better naming pattern: FooImple implements Foo
However, this only helps at the source code level since Java compiles keyword interfaces to a different binary. That means when you refactor Foo
from concrete to abstract clients that don't need a single character of code changed still have to be recompiled.
It's these BUGS in these particular languages that keeps people from being able put off formal abstracting until they really need it. You didn't say what language you're using but understand there are some languages that simply don't have these problems.
You didn't say what language you're using so I'll just urge you to analyze your language and situation carefully before you decide it's going to be keyword interfaces everywhere.
The YAGNI principle plays a key role here. But so does "Please make it hard to shoot myself in the foot".