PHP: использовать родительский класс как объявление типа в интерфейсе

Допустим, у меня есть конкретная модель, которая расширяется от «модели»

Class MyModel extends Model { ... }

Для каждой модели я хочу создать класс Translator, который реализует TranslatorInterface.

Interface TranslatorInterface {
    public function translate(Model $model);
}

Но вместо передачи родительского класса Model я хочу передать точную дочернюю модель в метод translate.

Class MyTranslator implements TranslatorInterface {
    public function translate(MyModel $model) { 
        ... 
    }
}

И я не могу заставить это работать.

Я знаю, что могу обойти это

Class MyTranslator implements TranslatorInterface {
    public function __construct(MyModel $model) {
        $this->model = $model;
    }

    public function translate() {
        $this->model->doSomething();
        ...
    }
}

И метод интерфейса __construct не определен, и метод translate () также не получает никаких аргументов.

Но мне интересно, есть ли возможность сделать это так, как я пробовал выше (а если нет, и это задумано, почему это плохая идея;))

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Symfony Station Communiqué - 7 июля 2023 г
Symfony Station Communiqué - 7 июля 2023 г
Это коммюнике первоначально появилось на Symfony Station .
Оживление вашего приложения Laravel: Понимание режима обслуживания
Оживление вашего приложения Laravel: Понимание режима обслуживания
Здравствуйте, разработчики! В сегодняшней статье мы рассмотрим важный аспект управления приложениями, который часто упускается из виду в суете...
Установка и настройка Nginx и PHP на Ubuntu-сервере
Установка и настройка Nginx и PHP на Ubuntu-сервере
В этот раз я сделаю руководство по установке и настройке nginx и php на Ubuntu OS.
Коллекции в Laravel более простым способом
Коллекции в Laravel более простым способом
Привет, читатели, сегодня мы узнаем о коллекциях. В Laravel коллекции - это способ манипулировать массивами и играть с массивами данных. Благодаря...
Как установить PHP на Mac
Как установить PHP на Mac
PHP - это популярный язык программирования, который используется для разработки веб-приложений. Если вы используете Mac и хотите разрабатывать...
2
0
57
2

Ответы 2

С точки зрения ООП: вы меняете сигнатуру функции:

В реализации также следует использовать:

public function translate(Model $model){}

И вы можете передать этой функции аргументы типа MyModel.

Все MyModel являются Model, но не все Model являются MyModel, у вас могут быть другие реализации Model, которые могут быть переданы функции, но не MyModel.

РЕДАКТИРОВАТЬ1: Согласно вашему комментарию ниже:

вы можете проверить каждую имплементацию, если она является экземпляром требуемого класса:

Class MyTranslator implements TranslatorInterface {
    public function translate(Model $model) { 
      if (is_a($model, 'MyModel')) {
      echo "yes, you use to translate MyModel \n";
      }
    }
}

В зависимости от контекста вы можете использовать это решение, но я думаю, что решение с использованием _constructor - лучший способ сделать это с небольшим изменением:

Class MyTranslator implements TranslatorInterface {
    public function __construct(Model $model) {
        $this->model = $model;
    }

    public function translate() {
        $this->model->doSomething();
        ...
    }
}

Вам нужен только один конструктор, и он будет использовать правильную имплементацию, основанную на полиморфизме.

Что ж, это мой вопрос ... Скажем, у меня есть 3 дочерних элемента ModelA, ModelB и ModelC. И для каждой модели у меня есть собственный переводчик TranslatorA, B и C также. ВНИМАНИЕ !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! Все переводчики могут просто обрабатывать конкретную модель TranslatorA, просто ModelA и так далее, но для этого необходима функция translate (). И я хочу предотвратить передачу неправильной модели в качестве аргумента, но хочу сказать, что, по крайней мере, в интерфейсе это должен быть какой-то родительский тип. Кажется, это невозможно? Я знаю, что это редкий случай, и еще один обходной путь / дополнение к моему решению - это TranslatorFactory - просто интересно, почему это запрещено.

bibamann 13.08.2018 13:37

Это запрещено, поскольку нарушает Принцип замены Лискова:

Contravariance of method arguments in the subtype.

Это означает, что метод можно изменить, но только если вы расширите ограничение типа. На данный момент PHP не поддерживает настоящий контравариантность. В настоящее время вы можете использовать только простой расширение типа параметра - это означает, что в реализации интерфейса вы можете опустить тип параметра.

Другие вопросы по теме