First of all, I think you are mixing up some things. It looks like your InvoiceGenerator class is a factory class. Its responsibility is to create and return an Invoice object. The Invoice object is not a dependency of the InvoiceGenerator (in which case it would indeed be better to pass the Invoice object as an argument, known as Dependency Injection).
However, as I said, the InvoiceGenerator class seems to be a factory class, and the whole idea of a factory class is that the logic for instantiating an object is encapsulated within that class (as in your second example). If you would pass the Invoice object as an argument (as in your first example), you would have to instantiate it somewhere else, and if you have multiple places in your code where this happens, you would end up duplicating the instantiating logic. The whole idea of the factory pattern is to prevent this.
By using a factory class you can encapsulate the object instantiation logic and avoid duplication. You can also put some more advanced logic into the InvoiceGenerator's create()
method to decide what type of object you want to return. For instance, you might want to return an Invoice
object if the total price is > 0
, but a CreditNote
object if the price is < 0
. Of course they should both extend the same interface (for instance InvoiceInterface
).
Also, it looks like you are delegating the actual initializing of the Invoice object to the object itself, since the create()
method of InvoiceGenerator does nothing more than calling the create()
method of the Invoice object, where the actual logic seems to take place. This violates the single responsibility principle, because the Invoice class is now both responsible for holding data about an invoice and setting all the data from an array. The latter should instead be the responsibility of the factory class.
So, I would create the class like this:
class InvoiceFactory
{
public function create(array $data)
{
if ($data['total'] >= 0) {
$invoice = new Invoice();
} else {
$invoice = new CreditNote();
}
$invoice->setTotal($data['total']);
// set other data using setters as well
return $invoice;
}
}
As you can see, there is no create()
method being called on the $invoice
object.