DEV Community

Discussion on: Domain-Driven Design - The Factory in PHP

Collapse
 
canderson93 profile image
Carl Anderson

Is there any reason we use a factory class instead over a factory function?

class TimeSpan
{
    /** @var \DateTimeImmutable **/
    private $from;

    /** @var \DateTimeImmutable **/
    private $until;

    private function __construct(\DateTimeImmutable $from, \DateTimeImmutable $until) 
    {
        if ( $from >= $until ) {
            throw new \InvalidArgumentException('Invalid time span.');
        }
        $this->from = $from;
        $this->until = $until;
    }

    // Some other useful stuff goes in here...

    public static function createFromConfiguration(\TimeSpanConfiguration $timeSpanConfiguration, \DateTimeImmutable $from, \DateTimeImmutable $until): TimeSpan
    {
        // We just ask the configuration if the given from-until time span is valid.
        // That way we don't need any getters on the configuration. Neat.
        if ( !$timeSpanConfiguration->isValidTimeSpanFromUntil($from, $until) ) {
            throw new \DomainException('This time span is too long!');
        }

        return new TimeSpan($from, $until);
    }
}

// Usage:
$config = new TimeSpanConfiguration(new \DateInterval('PT2D'));
$timeSpan = TimeSpan::createFromConfiguration($config, new \DateTimeImmutable('2019-02-17 17:00:00'), new \DateTimeImmutable('2019-02-17 18:00:00'));

I've not dealt much with factory classes, but naïvely a function seems cleaner to me -- That being said, I've noticed that the class pattern is very popular, so I was wondering if you could explain some of the reasoning behind it.

Collapse
 
coajaxial profile image
Fluttershy

You are absolutely right! It actually depends what you are constructing. Sometimes it's not that simple like in this example, sometimes you may have multiple dependencies that you need to create a class, that's where a own dedicated factory may be more maintainable than passing multiple dependencies to your constructor/factory method. Also, dependency injection is way easier if you have a dedicated factory class :)

Collapse
 
daanwilmer profile image
Daan Wilmer

In languages like Java or C# you can declare a certain method (including the constructor) to be only visible to the same package (Java) or project (C#). This makes sure you don't have to jump through the hoops of reflection to let the Factory use the constructor without making it available to the rest of the world. This makes it a lot cleaner.

The main reason you would want a separate class is separation of concerns: one class that deals with the logic of being a Thing (a TimeSpan, in this case), one class that deals with the logic of creating a thing (the TimeSpanFactory).
If the business logic for creating a Thing is more involved than a simple check, this is easier to contain in a ThingFactory class than in the Thing::factory(...) function.
If you need multiple different ways to create a Thing, for example if you have different types of configuration or if you want to mock the factory for testing, it's easier to have that in separate ThingFactory classes than to have it in the Thing.
There are a couple more reasons why you would want this separation, and most of them have to do with rising complexity of having it all in one class. I will concede that it's not always worth it in PHP, where it's a bit harder to separate the classes, but it does pay off as complexity increases.