Есть ли достойный обходной путь к отсутствию в Generics PHP, позволяющего статический контроль кода для выявления согласованности типов?
У меня есть абстрактный класс, который я хочу разделить на подклассы, а также обеспечить, чтобы один из методов изменился с получения параметра одного типа на параметр, который является подклассом этого параметра.
abstract class AbstractProcessor {
abstract function processItem(Item $item);
}
class WoodProcessor extends AbstractProcessor {
function processItem(WoodItem $item){}
}
Это не разрешено в PHP, потому что это изменяет сигнатуру методов, что недопустимо. С обобщением стиля Java вы можете сделать что-то вроде:
abstract class AbstractProcessor<T> {
abstract function processItem(T $item);
}
class WoodProcessor extends AbstractProcessor<WoodItem> {
function processItem(WoodItem $item);
}
Но, очевидно, PHP не поддерживает их.
Google для этой проблемы, люди предлагают использовать instanceof
для проверки ошибок во время выполнения, например
class WoodProcessor extends AbstractProcessor {
function processItem(Item $item){
if (!($item instanceof WoodItem)) {
throw new \InvalidArgumentException(
"item of class ".get_class($item)." is not a WoodItem");
}
}
}
Но это работает только во время выполнения, оно не позволяет вам проверять ваш код на наличие ошибок с помощью статического анализа - так есть ли какой-нибудь разумный способ обработки этого в PHP?
Более полный пример проблемы:
class StoneItem extends Item{}
class WoodItem extends Item{}
class WoodProcessedItem extends ProcessedItem {
function __construct(WoodItem $woodItem){}
}
class StoneProcessedItem extends ProcessedItem{
function __construct(StoneItem $stoneItem){}
}
abstract class AbstractProcessor {
abstract function processItem(Item $item);
function processAndBoxItem(Box $box, Item $item) {
$processedItem = $this->processItem($item);
$box->insertItem($item);
}
//Lots of other functions that can call processItem
}
class WoodProcessor extends AbstractProcessor {
function processItem(Item $item) {
return new ProcessedWoodItem($item); //This has an inspection error
}
}
class StoneProcessor extends AbstractProcessor {
function processItem(Item $item) {
return new ProcessedStoneItem($item);//This has an inspection error
}
}
Так как я передаю только Item
to new ProcessedWoodItem($item)
и он ожидает WoodItem в качестве параметра, проверка кода показывает, что произошла ошибка.
источник
Ответы:
Вы можете использовать методы без аргументов, документируя параметры с помощью doc-блоков:
Я не собираюсь говорить, что думаю, что это хорошая идея.
Ваша проблема в том, что у вас есть контекст с переменным числом членов - вместо того, чтобы пытаться использовать их в качестве аргументов, лучшая и более перспективная идея состоит в том, чтобы ввести контекстный тип для переноса всех возможных аргументов, чтобы аргумент список никогда не нужно менять.
Вот так:
Статические фабричные методы, конечно, являются необязательными, но могут быть полезны, если только определенные конкретные комбинации элементов создают значимый контекст. Если это так, вы можете также объявить себя
__construct()
защищенным / частным.источник