Static factory pattern with inheritance and type hints

514 Views Asked by At

I'm trying to make a class Developer which is a subclass of Person.
I want both of them to use the static factory pattern (or "named constructors").

I've seen some examples of that pattern, but none of them using inheritance.

Question 1
In the examples they make the constructor method always private.
Is it ok to make it protected in order to be called from the child constructor?
Or should I address the problem making the constructors always private, and trying to build the inheritance calling the parent's create method from the child's create method?

Question 2
When I try to instantiate either class Person or Developer, I'm getting the error below. Why?

PHP Fatal error:  Declaration of Developer::create(string $name, string $surname, ?int $yearsOfExperience = NULL, ?string $preferredLanguage = NULL): Developer must be compatible with Person::create(string $name, string $surname): Person in InheritanceTest.php on line 57

It works when I delete the : self type hints in both create methods, but I don't understand why are they incompatibles, if Developer is a child class of Person.

Thanks in advance.

<?php

class Person
{
    protected $name;
    protected $surname;

    protected function __construct(string $name, string $surname)
    {
        $this->name = $name;
        $this->surname = $surname;
    }

    public static function create(string $name, string $surname): self
    {
        // Some validation

        if($name == ''){
            throw new InvalidArgumentException('A person name can not be empty.');
        }

        if($surname == ''){
            throw new InvalidArgumentException('A person surname can not be empty.');
        }

        return new self($name, $surname);
    }
}

class Developer extends Person
{
    protected $yearsOfExperience;
    protected $preferredLanguage;

    protected function __construct(string $name, string $surname, ?int $yearsOfExperience, ?string $preferredLanguage)
    {
        parent::__construct($name, $surname);

        $this->yearsOfExperience = $yearsOfExperience;
        $this->preferredLanguage = $preferredLanguage;
    }

    public static function create(string $name, string $surname, ?int $yearsOfExperience = null, ?string $preferredLanguage = null): self
    {
        // Some validation

        if($yearsOfExperience < 0){
            throw new InvalidArgumentException('The years of experience can not be negative.');
        }

        if($preferredLanguage == ''){
            throw new InvalidArgumentException('The preferred language can not be empty.');
        }

        return new self($name, $surname, $yearsOfExperience, $preferredLanguage);
    }
}
1

There are 1 best solutions below

2
On

Question 1:

In the examples they make the constructor method always private. Is it ok to make it protected in order to be called from the child constructor?

You have to make it protected. Otherwise child will not be allowed to call the parent's method.

Question 2:

It works when I delete the : self type hints in both create methods, but I don't understand why are they incompatibles, if Developer is a child class of Person.

Try using static instead of self. It might work, but I am not sure. But you will still have notice (or warning, don't remember) because factory method in Developer has different parameters than Person. In PHP this is allowed but not recommended.