I prefer the "first approach" you described. Besides being more decoupled from your app's specific architecture, one reason is that you can reuse that code in Console Commands and Queued Jobs where you might not have access to the request object.
factory that depends on request parameters
Given that I have a factory class that create fruits based on a request parameter.
Should the client know the exact parameter that needs to be passed to the factory ( First Approach)
Or it is better to pass a generic parameter to the Factory ( in other words hide the specifics from the client ) and let the Factory know which exact parameter it needs to create an object. ( Second Approach )
First Approach
class FruitFactory
{
public function create($type)
{
if($type == 'Apple')
{
return new Apple();
}
}
}
$fruitFactory = new FruitFactory();
$type = request('type');
$fruit = $fruitFactory->create($type);
Second Approach
class FruitFactory
{
public function create(Request $request)
{
$type = request('type');
if($type == 'Apple')
{
return new Apple();
}
}
}
$fruitFactory = new FruitFactory();
$fruit = $fruitFactory->create();
Updated Question
If the FruitFactory is a factory of factories, does it need to know about all the exact parameters that its children factories need to create an object ?
Or maybe in this case the second approach is better ( Just passing the general $request parameter and let the factory find out which parameter it needs to create an object )
class FruitFactory
{
public function create($type, $color)
{
if($type == 'Apple')
{
return app(Apple::class)->create($color);
}
}
}
class Apple
{
public function create($color)
{
if($color == 'green')
{
return new GreenApple();
}
}
}
$fruitFactory = new FruitFactory();
$type = request('type');
$color = request('color');
$fruitFactory->create($type, $color);
Well, as I said before, the first approach would be my personal preference, but that doesn't mean the second approach is a bad idea.
If you have a complex "factory of factories" (or a resolver maybe), maybe passing the request object can do it.
Note that it will make it harder to use that "factory of factories" in other places where a request object might not be available (Console Commands, Queues Jobs, Tests, ...)
I would go for a DTO (Data Transfer Object) that knows to build itself from a request Object, and pass that to the "Factory of Factories" object.
That way I can manually construct the DTO in places where I don't have a request Object.
Something like this:
class MyDTO {
public $type;
public $color;
public function __construct($type, $color) {
$this->type = $type;
$this->color = $color;
}
public static function fromRequest($request) {
return new self($request->input('type'), $request->input('color'));
}
}
Then your "Factory of factories" can receive this DTO:
class FruitFactory
{
public function create($dto)
{
if($dto->type == 'Apple')
{
return app(Apple::class)->create($dto->color);
}
}
}
And you would call it like this:
$dto = MyDTO::fromRequest(request());
$fruitFactory = new FruitFactory();
$fruit = $fruitFactory->create($dto);
Notice that although we have a new class, the usage didn't get more complex.
The advantage is that if you need to use it outside a request context you can buid your DTO and use it:
$type = // from a file, for example
$color = // from an API response, for example
$dto = new MyDTO($type, $color);
$fruitFactory = new FruitFactory();
$fruit = $fruitFactory->create($dto);
Hope it helps somehow.
Please or to participate in this conversation.