Když getter nemá co vrátit
- David Grudl
- Nette Core | 8218
Řeším otázku, jak v Nette sjednotit přístup k tomu, když getter
nemá co vrátit. Může to být klidně
Nette\PhpGenerator\ClassType::getMethod()
nebo
BaseControl::getForm()
atd.
Nette převážně používalo tohle řešení:
public function getFoo(bool $need = true): ?Foo
{
if (!$this->foo && $need) {
throw new Exception("I don't have foo");
}
return $this->foo;
}
Má to výhodu v tom, že si vystačí s jednou metodou a podle situace se můžeme rozhodnout, zda chceme výchozí výjimku nebo vrátit null. Dříve byl určitou nevýhodou bool parametr, neboť bool parametry obecně jsou v kódu srozumitelné, ale tohle padlo od příchodu pojmenovaných argumentů. Větším problémem je statická kontrola, která nedokáže dvojí chování rozlišit a pak upozorňuje, že metoda vrací null. Nicméně PHPStan už to třeba umí:
/** @return ($need is true ? Foo: ?Foo) */
public function getFoo(bool $need = true): ?Foo
{
}
Dvojice has & get
Alternativní možnost je mít dvojici metod
- hasFoo(): bool
- getFoo(): Foo
Tedy getFoo()
bude vždy vyhazovat výjimku, které lze
předejít voláním hasFoo()
. Problém s tímto přístupem je,
že v některých situacích je ověřit existenci a získat
podobně složitý úkol, který se tak vlastně odehrává zbytečně dvakrát.
A z pohledu uživatele je tu nutnost volat obě metody se stejným
parametrem – pokud bych chtěl použít nějaký výraz, musel bych si jej
uložit do proměnné. Výhodou je existence funkce has
, která
vypadá srozumitelněji v podmínkách.
Dvojice get() a getIfExists()
Třetí možností je dvojice metod:
- getFoo(): Foo
- getFooIfExists(): ?Foo
První vyhazuje výjimku, druhá vrací null. Tahle varianta řeší
problémy zmíněné u dvojice has & get. Zkusil jsem tohle řešení kdysi
použít v Nette právě kvůli (tehdejším) problémům se statickou
analýzou, a to v Presenteru a třídě User, ale uvědomil jsem si, že se mi
vlastně něčím nelíbí… Že parametr $need
mi připadal
elegantnější.
Co si o tom myslíte vy?
- Infanticide0
- Člen | 103
/** @throws NotFoundException */
function get(): Foo;
function tryGet(): ?Foo;