Как реализовать паттерн «фабрика» в HackLang?

Дано: hhvm + код на Hack + //strict mode
Требуется: реализовать паттерн "фабрика"

<?hh // strict
namespace mynamespace\Uri;

abstract class UriAbstract 
{
    protected string $_scheme = '';

    public static function factory(string $uri = 'http') : this {
        $aUri = explode(':', $uri, 2);
        $scheme = (string)strtolower($aUri[0]);
        
        if ( strlen($scheme) === 0 ) {
            // throw Exception
        }
        
        if ( ctype_alnum($scheme) === false ) {
            // throw Exception
        }

        switch ($scheme) {
            case 'http':
            case 'https':
                $schemeHandler = new Http(); // Корень проблемы
                break;
            default:
                    throw new \Exception('Undefined scheme.');
        }
        return $schemeHandler;
    }
}
// --------------
namespace mynamespace\Uri;
class Http extends UriAbstract {};


Суть вопроса в том, что фабричный метод UriAbstract::factory должен возвращать одного из потомков UriAbstract, например Http, Https или любого другого.

Проблема в том, что в strict mode необходимо указать тип возвращаемых методом данных, но в этом случае "this" - не корректный тип, т.к. он соответствует UriAbstract, а не возвращаемому Http.
Динамическое создание объекта класса через meth_caller ( string $class_name , string $method_name ), т.е. через статический метод а-ля Http::getInstance() тоже не сработает, т.к. $class_name должен быть "ValidClass::class".

Меня устроил бы вариант, приведенный в примере (сводящийся по сути к return new Http();), но что тогда написать вместо this в качестве возвращаемого значения?
  • Вопрос задан
  • 2410 просмотров
Решения вопроса 1
maxru
@maxru Автор вопроса
Ответ оказался проще некуда :)
<?hh // strict
namespace mynamespace\Uri;

abstract class UriAbstract 
{
    protected string $_scheme = '';

    public static function factory(string $uri = 'http') : UriAbstract {
        $aUri = explode(':', $uri, 2);
        $scheme = (string)strtolower($aUri[0]);
        
        if ( strlen($scheme) === 0 ) {
            // throw Exception
        }
        
        if ( ctype_alnum($scheme) === false ) {
            // throw Exception
        }

        switch ($scheme) {
            case 'http':
            case 'https':
                $schemeHandler = new Http(); // Корень проблемы
                break;
            default:
                    throw new \Exception('Undefined scheme.');
        }
        invariant($schemeHandler instanceof UriAbstract, 'Instance of UriAbstract expected.'); // не обязательно, но лучше убедиться, что имеем "правильного" наследника.
        return $schemeHandler;
    }
}
// --------------
namespace mynamespace\Uri;
class Http extends UriAbstract {};
Ответ написан
Комментировать
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы