PHP přichází po delší době s novinkami, které mají potenciál významně změnit způsob, jak píšeme objektově orientovaný kód. Property hooks a asymmetric visibility představují elegantní řešení problémů, se kterými se PHP programátoři potýkají už roky. Zatímco dosud jsme byli prakticky nuceni používat gettery a settery pro jakoukoliv práci s daty objektu, nyní máme k dispozici elegantní nástroje pro přímou kontrolu přístupu k vlastnostem.
Property hooks umožňují definovat vlastní chování při čtení a
zápisu property (objektových, ne statických). Jde o mnohem čistší
řešení než dosavadní používání magických metod
__get/__set
, a to nejen syntakticky – engine je na hooky
speciálně optimalizovaný, takže jejich výkon je výrazně lepší.
Podívejme se na jednoduchý příklad. Představme si běžnou třídu
Person
s veřejnou property age
:
class Person
{
public int $age = 0;
}
$person = new Person;
$person->age = 25; // OK
$person->age = -5; // OK, ale to není správně!
PHP díky typu int
zajistí, že věk bude vždy celé číslo
(to lze od PHP 7.4). Ale co když chceme, aby věk nemohl být záporný? Bez
hooks bychom museli vytvořit getter a setter metody, a property by musela být
private. S hooks to vyřešíme elegantně:
class Person
{
public int $age = 0 {
set => $value >= 0 ? $value : throw new InvalidArgumentException;
}
}
$person->age = -5; // vyhodí InvalidArgumentException
Navenek se property chová stejně jako v prvním případě – čteme
i zapisujeme přímo $person->age
. Ale díky hooku máme plnou
kontrolu nad tím, co se při zápisu děje.
Obdobně můžeme vytvořit hook get
pro čtení. Hookům lze
také přidávat atributy. A samozřejmě můžou obsahovat plnohodnotný kód,
nikoliv jednoduchý výraz:
class Person
{
public string $first;
public string $last;
public string $fullName {
get {
return "$this->first $this->last";
}
set(string $value) {
[$this->first, $this->last] = explode(' ', $value, 2);
}
}
}
Důležité je, že jakýkoliv přístup k proměnné jde vždy přes hooky,
včetně toho, když se k $this->age
přistupuje v samotném
těle třídy Person
. Jen uvnitř samotného hooku přistupujete
přímo k reálné proměnné.
Zkušenosti z minulosti: co nás naučil SmartObject?
Pro uživatele Nette může být zajímavé ohlédnout se do minulosti. Framework totiž podobnou funkcionalitu nabízel už před 17 lety ve formě SmartObject, který výrazně vylepšoval práci s objekty v době, kdy PHP v této oblasti značně zaostávalo.
Pamatuju si, že tehdy přišla vlna bezbřehého nadšení, kdy se properties používaly prakticky všude. Tu pak vystřídala vlna opačná – nepoužívat je nikde. Důvod byl jednoduchý: neexistovalo jasné vodítko, kdy je lepší použít metody a kdy property. Současné nativní řešení je ale kvalitativně jinde. Property hooks a asymetrická viditelnost nejsou jen náhrada getterů a setterů – jsou to plnohodnotné nástroje pro objektový návrh, které nám dávají stejnou úroveň kontroly jako máme u metod. Díky tomu můžeme mnohem lépe rozlišit, kdy je property skutečně tím správným řešením.
Dvojí typ vlastností: backed vs virtual
Podívejte se na tento příklad a zkuste si rychle zodpovědět otázky:
- Je
$age
jen pro zápis nebo i pro čtení? - Je
$adult
jen pro čtení nebo i pro zápis?
class Person
{
public int $age = 0 {
set => $value >= 0 ? $value : throw new InvalidArgumentException;
}
public bool $adult {
get => $this->age >= 18;
}
}
Samozřejmě $age
je, jak jsme si řekli už dříve, property
pro čtení i pro zápis. Ale $adult
je jen pro čtení!
A tady narážíme na první zásadní problém v designu property hooks. Ze signatury property vůbec nepoznáme, zda do ní lze zapisovat nebo ji číst!
Odpověď se totiž skrývá v kódu, v implementaci hooků. Property totiž mohou být dvojího druhu: backed (se skutečným úložištěm v paměti) a virtuální (které pouze simulují existenci property). To, zda je property backed nebo virtuální, rozhoduje, zda se v kódu hooku na ni odkazujeme.
Property je backed (má vlastní úložiště), pokud:
- v těle hooku se vyskytuje odkaz na sebe, tj.
$this->propertyName
- nebo má zkrácený
set
, který znamená zápis do$this->propertyName
V našem příkladu:
- Property
$age
je backed, protože používá zkrácenýset
(což automaticky znamená zápis do$this->age
) - Property
$adult
je virtuální, protože žádný její hook neobsahuje$this->adult
Je to sice mazané řešení, ale nešťastné. Tak zásadní informaci, jako zda lze property číst nebo do ní zapisovat, má prozradit API a signatura na první pohled, ne až studium implementace.
Bezpečná práce s referencemi
Reference v PHP umožňují, aby dvě proměnné ukazovaly na stejné místo
v paměti. Když změníme jednu, změní se i druhá. Je to jako když máte
dva ovladače ke stejné televizi – ať použijete kterýkoliv, ovládáte tu
samou televizi. Vytvoření reference se dělá pomocí znaku
&
.
Pokud by někdo mohl získat referenci na property, která má
set
hook, mohl by její hodnotu měnit přímo, a tedy obejít
veškerou validaci. Podívejte se na příklad:
class Person
{
public int $age = 0 {
set => $value >= 0 ? $value : throw new InvalidArgumentException;
}
}
$ref = &$person->age; // Fatal error: Cannot take reference of property
$ref = -1; // kdyby to šlo, validace by byla obejita
PHP proto získání reference na takovou property vůbec nedovolí a vyhodí
chybu (myšleno na backed proměnnou se set
hookem). Je to
správné řešení – property hook má zajistit, že se do property dostane
jen platná hodnota, a reference by tuto kontrolu obcházely.
Nástrahy práce s poli
Když objekt obsahuje v property pole, můžeme přímo měnit nebo přidávat prvky:
class Person
{
public array $phones = [];
}
$person = new Person;
$person->phones[] = '777 123 456'; // přidá prvek na konec pole
$person->phones['bob'] = '777 123 456'; // přidá prvek s konkrétním klíčem
A právě tady narážíme na zajímavý problém s property hooks. Představme si, že chceme vytvořit třídu Person, která bude obsahovat seznam telefonních čísel, a chceme, aby se u nich automaticky ořezávaly mezery na začátku a konci:
class Person
{
public array $phones = [] {
set => array_map('trim', $value);
}
}
$person = new Person;
$person->phones[] = '777 123 456'; // Vyhodí Error: Indirect modification of Person::$phones is not allowed
Proč to nefunguje? Operace $person->phones[]
totiž v PHP
funguje ve dvou krocích:
- Nejdřív získá referenci na pole pomocí
get
- Pak do získaného pole přidá novou hodnotu
Tedy vůbec se nevolá set
hook. Ale především, jak jsme si
říkali, nelze získat referenci na backed proměnnou, pokud existuje
set
hook. Proto ta výjimka při pokusu o vložení
nového čísla.
Nepomůže nám ani přidat metodu addPhone()
, která zavolá
$this->phones[] = $phone;
, protože jak jsme si již řekli,
všechny přístupy k property (i uvnitř třídy) jdou přes hooky.
Mohli bychom zkusit jiný přístup:
$phones = $person->phones; // získáme pole
$phones[] = ' 777 123 456 '; // přidáme číslo
$person->phones = $phones; // uložíme zpět
To by sice fungovalo, ale představte si pole s tisíci čísly. Náš
set
hook by musel provést trim()
na všech číslech
znovu, i když se přidalo jen jedno. To není efektivní řešení.
Podle mě existuje jen jedna správná cesta a to uvědomit si, že pokud má pole nějak specificky pracovat se svými prvky (např. ořezávat mezery), musí to zajistit samotné pole, to není úkol pro objekt, který jen pole drží ve své property. Jistě, pole nemůžeme naučit nové funkcionalitě, ale můžeme ho emulovat přes objekt s rozhraním ArrayAccess:
class Phones implements ArrayAccess
{
private array $data = [];
public function __construct(array $data = [])
{
$this->data = array_map('trim', $data);
}
public function offsetSet(mixed $offset, mixed $value): void
{
$value = trim($value);
if ($offset === null) {
$this->data[] = $value;
} else {
$this->data[$offset] = $value;
}
}
// implementace metod offsetGet(), offsetExists(), offsetUnset()
}
class Person
{
function __construct(
public Phones $phones = new Phones,
) {}
}
$person = new Person;
$person->phones[] = ' 777 123 456 '; // číslo uloží se ořezané
Logika ořezávání je tam, kam patří – v objektu reprezentujícím pole telefonních čísel.
Property hook v tomto případě můžeme využít například k pohodlné
konverzi pole na tento objekt, tj. do $person->phones
bude
možné zapsat i pole:
class Person
{
function __construct(
public Phones $phones = new Phones {
set(array|Phones $value) => is_array($value) ? new Phones($value) : $value;
},
) {}
}
$person = new Person;
$person->phones = [' 888 999 000 ', '777 888 999']; // převede se na Phones a ořeže
Jak vidíte, hooks mohou obsahovat i promoted properties.
Existuje ještě druhá možnost řešení, a to použít virtuální
property. Připomeňme, že na rozdíl od backed property, nepoužívá
$this->propertyName
v těle hooku. Sice i nadále nebude
možné přidávat čísla pomocí $person->phones[] = ...
, ale
bude možné pro tento účel přidat metodu:
class Person
{
private array $_phones = []; // reálné úložiště telefonních čísel
public array $phones { // virtuální property
get => $this->_phones;
set {
$this->_phones = array_map('trim', $value);
}
}
public function addPhone(string $phone): void
{
$this->_phones[] = trim($phone);
}
}
$person = new Person;
$person->addPhone(' 777 123 456 '); // uloží se ořezané
Toto je řešení, kdy nadále využíváme klasické pole a trimování zůstalo úkolem třídy Person.
Dědičnost
Potomci mohou přidat hooks k vlastnostem, které dosud žádné neměly, nebo předefinovat jednotlivé existující hooks:
class Person
{
public string $email;
public int $age {
set => $value >= 0
? $value
: throw new InvalidArgumentException('Age cannot be negative');
}
}
class Employee extends Person
{
// Přidá hook k property, která žádný neměla
public string $email {
set => strtolower($value);
}
// Přepíše existující hook a přidá další kontrolu
public int $age {
set => $value <= 130
? parent::$age::set($value)
: throw new InvalidArgumentException('Age cannot be over 130'));
}
}
V příkladu vidíme, jak Employee
přidal hook pro email a
rozšířil validaci věku o horní limit. Při přístupu k původní
implementaci hooku v rodiči se používá neobvyklá syntaxe
parent::$prop::set()
nebo get()
. Ta přesně
vystihuje, co chceme – nejdřív se odkážeme na vlastnost v rodiči a pak
na její hook.
Hooks můžeme dokonce označit jako final
, v takovém
případě jej potomci nemohou přepsat. A podobně můžeme jako
final
označit celou property – pak ji potomci nemohou přepsat
žádným způsobem (ani přidat hooks, ani rozšířit její viditelnost).
Property v rozhraních a abstraktních třídách
Překvapivou novinkou je podpora property v rozhraních a abstraktních třídách. Představte si, že vytváříte rozhraní pro entity, které mají jméno. Doposud jsme museli definovat getter a setter metody:
interface Named
{
public function getName(): string;
public function setName(string $name): void;
}
Díky property hooks můžeme v rozhraní deklarovat přímo property, a to asymetricky – lze definovat samostatně požadavek na čtení a zápis:
interface Named
{
// implementující třída musí mít veřejně čitelnou property name
public string $name { get; }
}
class Person implements Named
{
public string $name; // obyčejná property
}
class Employee implements Named
{
public string $name {
get => $this->firstName . ' ' . $this->lastName;
}
private string $firstName;
private string $lastName;
}
Všimněte si zajímavého detailu – rozhraní Named
požaduje property pro čtení a implementující třída Person
poskytuje klasickou property, čitelnou i zapisovatelnou. To je v pořádku,
rozhraní definuje jen minimální požadavky.
Deklarace property v rozhraní vyžaduje použít klíčové slovo
public
, přestože jde o pleonasmus – vše v rozhraní je ze
své podstaty veřejné. U metod považuji uvádění public
za
nesmysl, u property je to však vyžadováno kvůli konzistenci syntaxe.
Překvapivá je samotná forma deklarace, která je silně svázaná se
syntaxí hooks. Zatímco ve třídě můžeme napsat jednoduché
public string $name
, v rozhraní musíme použít
public string $name { get; set; }
, aby byla property použitelná
pro zápis i čtení. Nicméně to jednoznačně vítám. U rozhraní je
žádoucí rozlišovat mezi property určenými pro čtení a pro zápis.
Velmi podobně to funguje i u abstraktních tříd. Ty navíc mohou deklarovat i protected properties a také mohou poskytnout výchozí implementaci některých hooků:
abstract class Person
{
abstract public string $name { get; }
abstract protected int $age { get; set; }
// Poskytujeme výchozí implementaci set hooku
abstract public string $email {
get;
set => Nette\Utils\Validators::isEmail($value)
? $value
: throw new InvalidArgumentException('Invalid email');
}
}
Kovariantní/kontravariantní property
Zajímavou vlastností property v rozhraních a abstraktních třídách je
podpora kovariance a kontravariance. Property, která má pouze hook
get
, může být kovariantní (v potomkovi může vracet
specifičtější typ). Property pouze s hookem set
může být
kontravariantní (může přijímat obecnější typ):
class Animal {}
class Dog extends Animal {}
interface AnimalHolder
{
public Animal $pet { get; }
}
class DogHolder implements AnimalHolder
{
// Vrací specifičtější typ - to je v pořádku
public Dog $pet { get; }
}
Jakmile má property oba hooky get
i set
, musí typ
zůstat stejný.
Asymetrická viditelnost
Další významnou novinkou, úzce související s property hooks, je možnost definovat rozdílnou viditelnost pro čtení a zápis vlastností. Asymetrická viditelnost je výrazně jednodušší koncept než hooks a přitom řeší mnoho situací, kde jsme dříve zbytečně sahali ke getterům a setterům. Zatímco hooks jsou mocnější nástroj umožňující vložit libovolnou logiku, pro častý případ „veřejně čitelné, interně zapisovatelné“ property je asymetrická viditelnost mnohem elegantnější řešení.
Představme si třeba třídu Person. Chceme, aby datum narození mohl číst kdokoliv, ale měnit ho mohla jen samotná třída:
class Person
{
public private(set) DateTimeImmutable $dateOfBirth;
}
První modifikátor public
určuje viditelnost pro čtení a
druhý private(set)
pro zápis. Property je veřejně čitelná,
ale zapisovat do ní lze jen z metod třídy Person
. Ve
skutečnosti je uvedení public pro čtení volitelné (protože veřejné
čtení je výchozí stav) a můžeme psát jen:
class Person
{
private(set) DateTimeImmutable $dateOfBirth;
}
Platí přirozené pravidlo, že viditelnost pro zápis nemůže být
širší než pro čtení. Tedy protected public(set)
je
zakázané.
A jak je to s dědičností? V PHP platí, že potomek může viditelnost property buď zachovat, nebo rozšířit z protected na public. To samé platí i pro asymetrickou viditelnost. Potomek tedy může například rozšířit viditelnost pro zápis z protected na public:
class Person
{
public protected(set) string $name;
}
class Employee extends Person
{
// lze rozšířit viditelnost pro zápis
public public(set) string $name;
}
Zajímavý je případ vlastnosti s private(set)
. Taková
vlastnost je automaticky final
, protože vlastnost s privátním
zápisem ze své podstaty znamená, že ji nikdo (ani potomek) nesmí měnit.
Proto ji ani nelze v potomcích předefinovat.
Jestliže hooks přinášejí nový způsob kontrolované manipulace s vlastnostmi, asymetrická viditelnost umožňuje definovat, kdo má k této manipulaci přístup. První řeší „co se má stát“, druhá „kdo to může udělat“. Jde o dvě různé věci, které se vzájemně doplňují. A lze je samozřejmě kombinovat:
class Person
{
private(set) DateTimeImmutable $birthDate {
set => $value > new DateTimeImmutable
? throw new InvalidArgumentException('Birth date cannot be in future');
: $value;
}
}
Tato property je veřejně čitelná, zapisovat do ní může pouze třída sama, a při zápisu se navíc kontroluje, zda datum není v budoucnosti.
Asymetrická viditelnost a problém s poli
Vzpomeňte si na příklad s telefonními čísly z předchozí kapitoly.
Tam jsme řešili problém s přidáváním čísel do pole a nakonec jsme
vytvořili speciální třídu Phones
. Asymetrická viditelnost
nám nabízí jiné elegantní řešení:
class Person
{
private(set) array $phones = [];
public function addPhone(string $phone): void
{
$this->phones[] = trim($phone);
}
}
$person = new Person;
var_dump($person->phones); // OK: lze číst
$person->addPhone('...'); // OK: lze přidat číslo
$person->phones = []; // CHYBA: nelze přepsat pole
V tomto případě nelze vůbec zapisovat do
$person->phones
, tato proměnná slouží jen ke čtení. Pro
přidání nového čísla máme metodu.
Pro úplnost dodejme, že u vlastnosti s omezeným zápisem nelze získat referenci zvenčí:
$ref = &$person->phones; // Fatal error: Cannot take reference
Reference jsou povolené jen ze scope, ze kterého je property zapisovatelná.
Jak je vidět, pro práci s polem v property máme několik možností, ale každá má své nevýhody:
- Použít objekt simulující pole (přináší overhead a složitější implementaci)
- Použít backed property s hookem (znemožňuje přímou modifikaci pole)
- Použít virtual property s privátním úložištěm (vyžaduje metody pro modifikaci pole)
- Použít asymetrickou viditelnost (přesouvá veškerou logiku do metod)
Neexistuje jednoznačně nejlepší řešení, vždy záleží na konkrétním případu a jeho požadavcích.
Modifikátor readonly a asymetrická viditelnost
Modifikátor readonly
jsou ve skutečnosti dva modifikátory
v jednom: zakazuje vícenásobný zápis a zároveň omezuje
zápis na private, tedy dělá private(set)
. To druhé mi
přišlo vždy zbytečně přísné a nepraktické. Proč by měla být
read-only proměnná automaticky privátní pro zápis? V praxi často
potřebujeme, aby ji mohly měnit i potomci, ale právě jen jednou.
V PHP 8.4 se to naštěstí změnilo. Nyní readonly
sám
o sobě dělá property protected(set)
, tedy zapisovatelnou
i z potomků. A když potřebujeme jinou viditelnost pro zápis, snadno ji
předefinujeme:
class Person
{
// read-only proměnná zapisovatelná jen uvnitř třídy (výchozí chování před 8.4)
public private(set) readonly string $name;
// read-only proměnná zapisovatelná i v potomcích (výchozí chování od 8.4)
public readonly string $dateOfBirth;
// read-only proměnná zapisovatelná kdekoliv
public public(set) readonly string $id;
}
Nekonzistence v terminologii
Všimli jste si ale určité nekonzistence v terminologii?
- stále se bavíme o properties pro čtení a zápis
- máme modifikátor
readonly
- nebo anotace
@property-read
a@property-write
- ale v hooks, interfaces a asymetrické viditelnosti
používáme
get/set
Bylo by logičtější používat termíny read
a
write
, ne? Chápu, že v případě hooks jde o konzistenci
s magickými metodami __get/__set
, ale podle mého naškodu,
termíny read/write
by dávaly větší smysl.
Zatímco u hooks má použití set
alespoň nějakou logiku
(jde o akci, která se má provést), v případě private(set)
už jde o pouhou snahu o konzistenci s hooks, která mi ale nedává smysl.
Asymetrická viditelnost je totiž koncepčně odlišná věc – neřeší
„co se má stát“ jako hooks, ale „kdo to může udělat“. Proto by
dávalo mnohem větší smysl použít termín write
, tedy
například private(write)
. Ten by byl nejen intuitivnější, ale
také by se lépe pároval s existujícím modifikátorem
readonly
.
class Person
{
private(write) string $name; // takto by to dávalo větší smysl
private(set) string $name; // takto to bohužel je
}
Zdá se, že PHP v honbě za syntaktickou konzistencí mezi hooks a asymetrickou viditelností obětovalo sémantickou konzistenci s již existujícími koncepty v jazyce.
Nová éra v PHP
V PHP světě jsme byli dlouho odkázáni na jediný správný způsob objektově orientovaného návrhu: všechny vlastnosti private a přístup k nim výhradně přes gettery a settery. Bylo to dáno tím, že public property byly problematické:
- neměli jsme nad nimi žádnou kontrolu, kdokoliv mohl změnit jejich hodnotu
- byly součástí veřejného API, tedy jakákoliv změna (přidání validace, změna typu) znamenala BC break
- nešly deklarovat v rozhraních
Proto každý, kdo chtěl programovat správně, používat rozhraní a dependency injection, musel sáhnout po getterech a setterech. Byly to jediné cesty, jak mít plnou kontrolu nad přístupem k datům objektu.
S příchodem PHP 8.4 se ale situace radikálně mění. Property hooks a asymetrická viditelnost nám dávají nad property stejnou úroveň kontroly jako máme u metod. Property můžeme bez obav použít jako součást veřejného API, protože:
- můžeme kdykoliv přidat validaci nebo transformaci hodnot
- můžeme kontrolovat, kdo může hodnoty měnit
- můžeme je deklarovat v rozhraních
V podstatě můžeme vnímat property hooks jako syntaktický cukr pro gettery a settery, který ale výrazně zpřehledňuje kód. Nebo naopak gettery a settery jako zbytečný boilerplate.
Ze své vlastní zkušenosti s Nette můžu mluvit velmi konkrétně – jak jsem říkal, framework podobnou funkcionalitu nabízel už před 17 lety. To znamená, že jsem měl možnost s property přístupem pracovat dlouho. A musím říct, že to bylo nesmírně návykové. Porovnejte běžný způsob zápisu a ten s využitím property:
// tradiční přístup
$this->getUser()->getIdentity()->getName()
// s využitím property
$this->user->identity->name
Ten druhý zápis je nejen kratší, hezčí, čitelnější, ale také velmi návykový.
Jistě, můžeme namítnout, že přímý přístup k datům objektu může svádět k porušování principů objektově orientovaného návrhu. Že místo ptaní se objektu na data bychom ho měli požádat o provedení operace (Tell-Don't-Ask princip). To je legitimní argument – ale platí především pro objekty s bohatým chováním, které implementují business logiku. Naproti tomu pro datové transfer objekty, value objects nebo configuration objects je přímý přístup k datům naprosto přirozený.
Přesto existuje nové dilema. Pokud máte existující knihovnu nebo framework, který důsledně používá gettery a settery, bylo by nešťastné do něj najednou zavádět property. Rozbili byste tím konzistenci API – uživatel by musel hádat, kde použít metodu a kde property. V takovém případě je možná lepší se držet zavedeného stylu.
Předpokládám, že se postupně vytvoří nové programátorské styly a konvence. Některé projekty možná zůstanou u getterů a setterů, jiné budou hledat cesty jak začlenit property.
Kromě rozhodnutí mezi property a metodou je důležité i jejich
pojmenování. Pro boolean hodnoty se často používají prefixy
is
nebo has
. Zatímco u metod jsou tyto prefixy
přirozené (isEnabled()
, hasComments()
),
u properties působí redundantně. Proto je lepší používat přímo
podstatná jména nebo adjektiva ($enabled
,
$commented
).
Kdy použít property a kdy metody?
Ale co když začínáte nový projekt? Kdy použít property a kdy metody? To není snadná otázka, ale můžeme se inspirovat v jazycích, které s property pracují už desetiletí (C#, Kotlin):
- Property se výborně hodí pro:
- value objects a datové transfer objekty
- konfigurační objekty
- jednoduché entity, kde jde primárně o data
- computed hodnoty, které jsou funkcí jiných property
- případy, kdy potřebujete jen základní validaci nebo transformaci hodnot
- Metody jsou lepší pro:
- operace pracující s více property najednou
- operace s vedlejšími efekty (logování, notifikace)
- akce, které něco dělají (save, send, calculate…)
- komplexní validace nebo business logiku
- operace, které mohou selhat z více důvodů
- situace, kde oceníme fluent interface
Všechna tato doporučení se nám snaží říct jednu základní věc: za použitím property by se neměl skrývat žádný složitý proces nebo něco, co má vedlejší efekty. Složitost operace by měla zhruba odpovídat tomu, co intuitivně očekáváme od čtení či zápisu do proměnné.
Jenže… když se podíváme na jedno z nejznámějších webových API
vůbec, totiž DOM v prohlížeči, zjistíme, že za zdánlivě jednoduchou
operací element.innerHTML = '...'
se skrývá jeden
z nejkomplexnějších procesů, jaké prohlížeč provádí – parsování
HTML, vytvoření nového DOM stromu, jeho napojení do existující struktury,
přepočítání layoutu, reflow, repaint… Přesto je tento přístup
považován za zcela intuitivní a přirozený.
Takže možná ty hranice nejsou tak ostré, jak se může zdát. Důležitější než samotná složitost implementace je to, zda daná operace _konceptuálně_ odpovídá čtení či zápisu nějaké vlastnosti. Pokud ano, property je na místě, i kdyby za ní byla sofistikovaná logika. Pokud ne, sáhněme po metodě, i kdyby byla triviální.
Napište komentář