Jak správně vyřešit problém s tzv. „circular reference“?
- fary
- Člen | 155
Ahoj, chtěl bych se zeptat, jak správně řešit problém, když máte svě service třídy a stane se vám, že první injektuje druhou a druhá injektuje první?
např.:
class A
{
private $b;
public function __construct(B $b)
{
$this->b = $b;
}
}
class B
{
private $a;
public function __construct(A $a)
{
$this->a = $a;
}
}
- GEpic
- Člen | 566
fary napsal(a):
Ahoj, chtěl bych se zeptat, jak správně řešit problém, když máte svě service třídy a stane se vám, že první injektuje druhou a druhá injektuje první?
např.:
class A { private $b; public function __construct(B $b) { $this->b = $b; } } class B { private $a; public function __construct(A $a) { $this->a = $a; } }
Proboha, proč a kde se tohle dá využít? :-O
- Šaman
- Člen | 2665
Tak občas se na to narazí, i když to není přímo zamýšlený závrhový vzor. Typicky se to občas řešilo když se někdo pokoušel o chytřejí autentizaci, která potřebovala entitu uživatele. A Nette\User zase vyžaduje autentizátor. Předáš autentizátoru objekt User a bum, hlásí to kruhovou závislost.
- fary
- Člen | 155
Konkrétně mám v úmyslu vytvořit cache pro menu. Mám několik hlavních sekcí v tom menu (články, videa, obrázky…). Aplikace obsahuje tagy a tyto tagy se přidávají záznamům, které můžou být tedy článek, obrázek nebo viedo. Ke každému záznamu se přiřadí vždy jeden tag. Takže pak, když generuji to menu, potřebuju nejdříve získat seznam všech existujících tagů a poté iterovat napříč všemi články, videi a obrázky (vždycky zvlášť pro každou sekci) a zjistit, jestli se pro daný tag v dané sekci vyskytuje nějaký záznam. Pokud ano, tak ten tag do menu přidám.
Ten popis výše je spíše jen jako menší doplnění, ale jde o to, že mám třídu pro cache, která injektuje repozitáře, které obsahují články, obrázky a videa, abych mohl získat data z db a uložit do cache. Pak do každého z těchto repozitářů injektuji tuhle cache třídu a zde je ten problém. Injektuji ji tam kvůli tomu, aby se invalidovala konkrétní menu sekce v cache, pokud někdo v této sekci změní záznamy (např. přidá článek).
class RepoA
{
private $menuCache;
public function __construct(MenuCache $menuCache)
{
$this->menuCache = $menuCache;
}
public function create()
{
// smazat sekci pro 'sekci A' v menu cache
}
}
class RepoB
{
private $menuCache;
public function __construct(MenuCache $menuCache)
{
$this->menuCache = $menuCache;
}
public function create()
{
// smazat sekci pro 'sekci B' v menu cache
}
}
class RepoC
{
private $menuCache;
public function __construct(MenuCache $menuCache)
{
$this->menuCache = $menuCache;
}
public function create()
{
// smazat sekci pro 'sekci C' v menu cache
}
}
class MenuCache
{
private $a;
private $b;
private $c;
public function __construct(RepoA $a, RepoB $b, RepoC $c)
{
$this->a = $a;
$this->b = $b;
$this->c = $c;
}
public function getAll()
{
// získat data z cache, pokud nejsou v cache, získat data z db a uložit do cache
}
}
- mkoubik
- Člen | 728
Spíš bych vyhodil závislost na cache z těch repozitářů a invalidoval to událostí.
class RepoA
{
public $onItemCreated = [];
public function create()
{
// ...
$this->onItemCreated($item);
}
}
class MenuCache
{
public function __construct(RepoA $repoA, RepoB $repoB, RepoC $repoC)
{
//
}
public function getAll()
{
// ...
}
public function invalidateItem($item)
{
// ...
}
}
$repoA->onItemCreated[] = [$menuCache, 'invalidateItem'];
- fary
- Člen | 155
Díky za rady, nakonec jsem to vyřešil tak, že jsem si v MenuCache vytvořil pro repozitáře settery.
class RepoA
{
private $menuCache;
public function __construct(MenuCache $menuCache)
{
$this->menuCache = $menuCache->setRepoA($this);
}
public function create()
{
// smazat sekci pro 'sekci A' v menu cache
}
}
class RepoB
{
private $menuCache;
public function __construct(MenuCache $menuCache)
{
$this->menuCache = $menuCache->setRepoB($this);
}
public function create()
{
// smazat sekci pro 'sekci B' v menu cache
}
}
class RepoC
{
private $menuCache;
public function __construct(MenuCache $menuCache)
{
$this->menuCache = $menuCache->setRepoC($this);
}
public function create()
{
// smazat sekci pro 'sekci C' v menu cache
}
}
class MenuCache
{
private $a;
private $b;
private $c;
public function setRepoA(RepoA $a)
{
$this->a = $a;
return this;
}
public function setRepoB(RepoB $b)
{
$this->b = $b;
return this;
}
public function setRepoC(RepoC $c)
{
$this->c = $c;
return this;
}
public function getAll()
{
// získat data z cache, pokud nejsou v cache, získat data z db a uložit do cache
}
}