Czy istnieje jakieś przyzwoite rozwiązanie problemu braku generycznych wersji PHP, które umożliwiają statyczną kontrolę kodu w celu wykrycia spójności typu?
Mam klasę abstrakcyjną, którą chcę podklasować, a także wymuszam zmianę jednej z metod z przyjmowania parametru jednego typu na przyjmowanie parametru, który jest podklasą tego parametru.
abstract class AbstractProcessor {
abstract function processItem(Item $item);
}
class WoodProcessor extends AbstractProcessor {
function processItem(WoodItem $item){}
}
Nie jest to dozwolone w PHP, ponieważ zmienia podpis metod, co jest niedozwolone. Za pomocą ogólnych stylów Java możesz zrobić coś takiego:
abstract class AbstractProcessor<T> {
abstract function processItem(T $item);
}
class WoodProcessor extends AbstractProcessor<WoodItem> {
function processItem(WoodItem $item);
}
Ale oczywiście PHP ich nie obsługuje.
Google w przypadku tego problemu, ludzie sugerują używanie instanceof
do sprawdzania błędów w czasie wykonywania, np
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");
}
}
}
Ale to działa tylko w czasie wykonywania, nie pozwala na sprawdzenie kodu pod kątem błędów przy użyciu analizy statycznej - więc czy jest jakiś rozsądny sposób radzenia sobie z tym w PHP?
Bardziej kompletnym przykładem problemu jest:
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
}
}
Ponieważ przekazuję tylko Item
do new ProcessedWoodItem($item)
i oczekuje on WoodItem jako parametru, kontrola kodu sugeruje, że wystąpił błąd.
źródło
Odpowiedzi:
Możesz użyć metod bez argumentów, zamiast tego dokumentując parametry za pomocą bloków doc:
Nie powiem jednak, że to dobry pomysł.
Twój problem polega na tym, że masz kontekst ze zmienną liczbą członków - zamiast próbować wymusić ich jako argumenty, lepszym i bardziej przyszłościowym pomysłem jest wprowadzenie typu kontekstowego, aby przenosić wszystkie możliwe argumenty, tak aby argument lista nigdy nie musi się zmieniać.
Tak jak:
Statyczne metody fabryczne są oczywiście opcjonalne - ale mogą być przydatne, jeśli tylko określone kombinacje elementów tworzą znaczący kontekst. Jeśli tak, możesz również zadeklarować, że jesteś
__construct()
chroniony / prywatny.źródło