Vývoj softwaru často přináší dilema. Například jak řešit situace, kdy getter nemá co vrátit. V tomto článku prozkoumáme tři strategie pro implementaci getterů v PHP, které ovlivňují strukturu a čitelnost kódu, a každá má své specifické výhody i nevýhody. Pojďme se na ně podrobněji podívat.
Univerzální getter s parametrem
Prvním a v Nette používaným řešením je vytvoření jediné getter metody, která, pokud hodnota není dostupná, může dle potřeby vrátit buď null nebo vyhodit výjimku. O chování rozhoduje volitelný parametr. Zde je příklad, jak by mohla metoda vypadat:
public function getFoo(bool $need = true): ?Foo { if (!$this->foo && $need) { throw new Exception("Foo not available"); } return $this->foo; } Hlavní výhodou tohoto přístupu je, že eliminuje potřebu mít několik verzí getteru pro různé scénáře použití. Někdejší nevýhodou byla horší srozumitelnost uživatelského kódu používajícího booleovské parametry, ale ta padla s příchodem pojmenovaných parametrů, kdy lze psát getFoo(need: false).
Dále tento přístup může způsobit komplikace v oblasti statické analýzy, jelikož dle signatury se zdá, že getFoo() může vrátit null v každé situaci. Nicméně nástroje jako PHPStan umožňují explicitní dokumentaci chování metody pomocí speciálních anotací, které zlepšují porozumění kódu a jeho správnou analýzu:
/** @return ($need is true ? Foo : ?Foo) */ public function getFoo(bool $need = true): ?Foo { } Tato anotace jasně určuje, jaké návratové typy může metoda getFoo() generovat v závislosti na hodnotě parametru $need. Ale například PhpStorm jí nerozumí.
Dvojice metod: hasFoo() a getFoo()
Další možností je rozdělit zodpovědnost na dvě metody: hasFoo() pro ověření existence hodnoty a getFoo() pro její získání. Tento přístup zvyšuje přehlednost kódu a je intuitivně srozumitelný.
public function hasFoo(): bool { return (bool) $this->foo; } public function getFoo(): Foo { return $this->foo ?? throw new Exception("Foo not available"); Hlavním problémem je redundance, zvláště v případech, kdy je kontrola dostupnosti hodnoty sama o sobě náročným procesem. Pokud hasFoo() provádí složité operace k ověření, zda je hodnota dostupná, a tato hodnota je poté opět získávána pomocí getFoo(), dojde k jejich opětovnému provedení. Hypoteticky může být stav objektu nebo dat změněn mezi voláním hasFoo() a getFoo(), což může vést k nesrovnalostem. Z uživatelského pohledu může být tento přístup méně pohodlný, protože nás nutí volat dvojici metod s opakujícím se parametrem. A nemůžeme využít například null-coalescing operátor.
Výhodou je, že některé nástroje pro statickou analýzu umožňují definovat pravidlo, že po úspěšném volání hasFoo() nedojde v getFoo() k vyhození výjimky.
Metody getFoo() a getFooOrNull()
Třetí strategií pro je rozdělení funkcionality na dvě metody: getFoo() pro vyhození výjimky, pokud hodnota neexistuje, a getFooOrNull() pro vrácení null. Tento přístup minimalizuje redundanci a zjednodušuje logiku.
public function getFoo(): Foo { return $this->getFooOrNull() ?? throw new Exception("Foo not available"); } public function getFooOrNull(): ?Foo { return $this->foo; } Alternativou je dvojice getFoo() a getFooIfExists(), ale v tomto případě nemusí být zcela intuitivní pochopit, která metoda vyhazuje výjimku a která vrací null. O trošku výstižnější by byla dvojice getFooOrThrow() a getFoo(). Další možností je getFoo() a tryGetFoo().
Každý z představených přístupů k implementaci getterů v PHP má své místo v závislosti na specifických potřebách projektu a preferencích vývojářského týmu. Při výběru vhodné strategie je důležité zvážit, jaký dopad bude mít na čitelnost, údržbu a výkon aplikace. Volba by odrážet snahu o co nejsrozumitelnější a nejefektivnější kód.