The original Foo
does three things:
$this->bar = new Bar(new Quz(rand($min, $max))
- defines
calc()
as new Quz(rand($min, $max))
- remembers
$min
and $max
so calc doesn't need them passed
The original Foo
is already doing dependency injection. It's primarily construction. The rand
thing is a touch of behavior and I really like to separate behavior from construction so having that hard coded in construction is a bit weird for me but not my chief gripe.
My chief gripe is I don't see any of this getting used. This reads like a contrived example. I really can't see that any of this is helping.
But fine. I'll imagine how the original Foo
is used. Say I need a Bar
made the way Foo
makes a Bar
. Oh, and I need some Quz
's. So I do this:
$foo = new Foo(1,10);
$bar = $foo.bar;
$quz1 = $foo.calc();
$quz2 = $foo.calc();
$quz3 = $foo.calc();
All of that is construction code. All it bought me was not having to write it like this:
How can I implement the above class with DI but without factories?
$min = 1;
$max = 10;
$bar = new Bar(new Quz(rand($min, $max));
$quz1 = new Quz(rand($min, $max));
$quz2 = new Quz(rand($min, $max));
$quz3 = new Quz(rand($min, $max));
Which, I swear to god, is actually easier to read. It's also amazingly flexible. I'll inject what I please. I'll use whatever method I like on $min and $max.
But fine, let's see what we get when we switch to the improved Foo
:
$foo = new Foo(new BarFactoryDefault(), new QuzFactoryDefault());
$foo.init(1,10);
$bar = $foo.bar;
$quz1 = $foo.calc();
$quz2 = $foo.calc();
$quz3 = $foo.calc();
Or am I doing it terribly wrong?
Yes
In addition to the above being more complicated than either previous solution I have yet to create these default factory classes, let alone the interfaces for them. Yes this is terribly wrong.
What your doing is reaching out to patterns and techniques you've heard were good and thought that by using good ingredients you were going to cook good food. Always taste the food you cook before you serve it. I like ketchup and I like milk. I don't like ketchup in my milk. That's not a problem with either the ketchup or the milk.
The problem here is the example is anemic. The problems these techniques solve aren't in this example. So trying to explore the techniques here becomes a festival of over engineering that doesn't teach you anything good.
You asked some other questions which I'll deal with next but the main point I want to make here is don't design these classes without thinking about how they're used.
Are factories required when doing dependency injection?
No.
Dependency injection doesn't require factories. Factories are one of many construction patterns. Dependency injection doesn't even require construction patterns. You can do it in main. Factories might perform injections. Factories can be injected. Factories abstract details of construction. You mostly use Factories to hide dependencies you want to treat as reasonable defaults. Least I do.
Do I really need a factory method or class for each class?
You need a factory method for each construction ceremony you are sick of looking at. It's crazy to think there is some 1 to 1 relationship here. It's zero to many.
Do I indeed need the init() function to replace the constructor?
I think it's an unpardonable sin that you let Foo
exist in a non-initialized state. There are many ways to fix that. Fold it into the constructor. Break this up into two objects. Builder patterns. Just find some way to not force me into holding on to something I can't use if I don't follow it's ceremony.
Sorry this has turned into such a rant. I remember wading through design patterns and wondering what the fuss was about. Bad examples like this were a big reason it took me so long to get it. Please don't accept that this stuff is good until you can see why it's good. Faith is a killer here. Taste the food before you serve it.