Yes, and it's also a good argument for separation of concerns, so you can more freely compose factories. When factories come as implementations of two or more factories in a single object, they don't compose as well - to reimplement part of it, you would need a work-around, such as delegating one method.
Look:
interface FooFactory { public function createFoo(); }
interface BarFactory { public function createBar(); }
interface LolFactory extends FooFactory, BarFactory {}
This scenario is problematic. You effectively end up with *two* abstractions for factories for *each* entity type, e.g. both FooFactory and LolFactory create Foo entities. You may be assuming the class implementations will compose exactly like the interfaces, but that's not a given - interfaces do not dictate a class hierarchy.
In practice, that means, if a consumer requires LolFactory, and I want to implement a custom factory for creation of a Foo or Bar entity, which forces me to implement a class with two methods, one of them unrelated to what I'm trying to do - it's a clear consequence of violating SRP... look - all this:
class MyLolFactory implements LolFactory
{
private $bar_factory;
public function __construct(BarFactory $other) {
$this->bar_factory = $other;
}
public function createFoo() {
return new Foo(...);
}
public function createBar() {
return $this->bar_factory->createBar();
}
}
As opposed to just this:
class MyFooFactory implements FooFactory {
public function createFoo() {
return new Foo(...);
}
}
And on the consumer side:
class Something {
public function __construct(LolFactory $lol) {
// ... code you have to read to figure out what it's using LolFactory for ...
}
// ... more code you have to read ...
}
As opposed to:
class Something {
public function __construct(FooFactory $foo_factory, BarFactory $bar_factory) {
// ... code you DON'T need to read, as each factory has one well-defined use!
}
}
I understand you want convenience, but this kind of convenience is extremely superficial - the convenience of typing out fewer type-hints comes at the cost of simplicity, and as you can clearly see from these examples, this is the worst kind of complexity: the kind that *spreads* to all your code.
Long-term, this will make things more complex and less transparent - if typing out your constructors and bootstrapping is a little more work, it's going to pay itself back in the form of much clearer separation, simpler composition, easier testing, mocking, etc...
Convenience isn't one of the SOLID principles, guys - but interface segregation is, e.g. "many client-specific interfaces are better than one general-purpose interface". Isn't that precisely what we're discussing here?
I really, really think that favoring convenience over tried-and-tested principles would be a mistake here.
And I think you're exaggerating the inconvenience of having to inject a few factories to begin with. It's not a big deal.
Why don't you try simple first, and see if that makes your head explode? I bet it won't, but if it does, and you feel you must, we can start talking about which principles we're willing to break to make the experience less painful. For the moment, I'm betting you will find that it's actually quite painless - most middleware components probably won't have more than 3 or at the most 4 dependencies...