#php #inheritance #covariant-return-types
#php #наследование #ковариантные типы возврата
Вопрос:
У меня есть такой код, как показано ниже. Я постарался упростить код, чтобы его было как можно проще понять.
class BaseFooClass { protected $keys = []; private $map = []; public function __construct($keyValuePairs) { foreach($this-gt;keys as $key =gt; $value) { $this-gt;map[$key] = $keyValuePairs[$key] ?? null; } } } class ChildFooClass1 extends BaseFooClass { protected $keys = ['foo1_a', 'foo1_b']; } class ChildFooClass2 extends BaseFooClass { protected $keys = ['foo2_a', 'foo2_b', 'foo2_c']; } //... (there are like a hundred child foo classes) abstract class BaseBarClass { protected $classIndex; protected function getFooBase(int $dataIndex) : ?BaseFooClass { // GetRemoteData is assumed to be a global function, the important thing here is the retrieved data depends on classIndex and dataIndex // If $classIndex is 1, the $keyValuePairs will look like ['foo1_a' =gt; value1, 'foo1_b' =gt; value2] where value1 and value2 depend on $dataIndex $keyValuePairs = GetRemoteData($this-gt;classIndex, $dataIndex); if (checkDataIntegrity($keyValuePairs)) { $class = "ChildFooClass" . $this-gt;classIndex; return new $class($keyValuePairs); } return null; } } class ChildBarClass1 extends BaseBarClass { protected $classIndex=1; public function getFoo(int $dataIndex) : ?ChildFooClass1 { // this line violates covariance/contravariance rule return $this-gt;getFooBase($dataIndex); } } class ChildBarClass2 extends BaseBarClass { protected $classIndex=2; // input to getFoo in each BarClass can be different public function getFoo($someInput) : ?ChildFooClass2 { $dataIndex = $this-gt;calculateDataIndex($someInput); // this line also violates covariance/contravariance rule return $this-gt;getFooBase($dataIndex); } }
Три критерия, которым я хочу соответствовать, это:
(1) Я хочу убедиться, что ChildBarClass1::getFoo возвращает только ChildFooClass1, но также убедиться, что BaseBarClass::getFooBase возвращает только класс, наследующий базовый класс. Эмпирическое правило: сделайте объявление типа как можно более строгим.
(2) Я хочу убедиться, что нет повторяющегося кода. Я не хочу вызывать GetRemoteData и checkDataIntegrity в каждой отдельной функции getFoo. На самом деле в реальном коде гораздо больше вещей, чем в этих двух.
(3) Я хочу придерживаться правила ковариации/контравариантности. В настоящее время третья последняя строка, показанная в коде, нарушает это правило, поскольку в ней используется функция, возвращающая родительский класс foo.
Кажется, я не могу придумать хорошее решение. Все, что я придумаю, либо нарушит правила, либо сделает код похожим на «взлом» и действительно уродливым. Если кто-нибудь может предложить хороший способ решить эту проблему, не нарушая ни одного из трех критериев или не изменяя общую структуру (т. Е. У каждого класса Foo есть соответствующий класс, обрабатывающий создание объекта Foo), я буду очень признателен.
Ответ №1:
Учитывая ваш код, вы можете просто удалить возвращаемый тип из getFooBase()
(или с помощью php v8 сделать это : mixed
). Этот метод не является частью общедоступного api. И поэтому здесь нет никаких реальных потерь.
Поскольку getFoo()
возвращаемые значения s вводятся индивидуально, вы все равно получите ошибку типа, если getFooBase()
возвращаемое значение не подходит.