Kompozitní objekt obsahující entitu i služby

Upozornění: Tohle vlákno je hodně staré a informace nemusí být platné pro současné Nette.
artemevsin
Člen | 61
+
0
-

Ahoj,
dotaz nesouvisí přímo s Nette, ale spíše všeobecně s návrhem. Nicméně na stackoverflow jsem se názoru nedočkal, tak prosím o radu zkušenějších zde.

Mám několik malých knihoven, které řeší konkrétní věci – třeba ImagesManager, TagsManager apod. Pak mám také malé knihovny typu MachinesManager, které spravují jednotlivé stroje a jejich parametry. Všechny knihovny vytváří entity (např. Image), které mají přiřazené vygenerovné UUID, pomoci kterého spolu komunikují:

<?php

interface IUuidUser {
	public function getUuid();
}

/**
 * @return Image[]
 */
class ImagesManager {
	public function findImages(IUuidUser $user) {
		return $this->dataSource->findByUuid($user->getUuid());
	}
}

class Machine implements IUuidUser {

}

// použití
class TestPresenter {
	public function test()
	{
		$machine = new Machine();
		$machineImages = $this->imageManager->findImages($machine);
	}
}

?>

Jelikož vím, že stroje mohou mít obrázky a např. tagy, tak bych chtěl entitu Machine zaobalit do kompozitní třídy MachineCompositeObject, která dostane v konstruktoru všechny potřebné služby a budu tak moct vytvořit hezčí rozhraní pro práci s Machine.

<?php

class MachineCompositeObject {
	private $machine;
	private $imagesManager;
	private $tagsManager;

	public function __construct(Machine $machine, ImagesManager $imagesManager, TagsManager $tagsManager)
	{
		$this->machine = $machine;
		$this->imagesManager = $imagesManager;
		$this->tagsManager = $tagsManager;
	}

	public function getName()
	{
		return $this->machine->getName();
	}

	public function getImages()
	{
		return $this->imagesManager->findImages($this->machine);
	}

	public function getTags()
	{
		return $this->tagsManager->findTags($this->machine);
	}
}

?>

Je to vlastně taková fasáda, která se vytváří ale pro každou entitu.

Je podle vás ten návrh takto v pořádku?

Budu rád za jakékoliv názory :)

CZechBoY
Člen | 3608
+
0
-

No chybí ti tu asi nejdůležitější část a to jak budeš to monstrum se 3 velkejma závislostma vytvářet.
Můžeš teda vytvořit továrničku, kterou si injectneš do repozitáře, vracející/vytvářející tenhle objekt.

artemevsin
Člen | 61
+
0
-

S továrničkou počítám.

Teď mě ještě napadlo, že možná by bylo lepší vytvořit čistě službu MachineService do které zapouzdřím potřebné služby…

Jestli to není čistší řešení a nebudou se mi zbytečně vytvářet nové instance pro každý stroj

<?php

class MachineService {
	public function __construct(ImagesManager $imagesManager, TagsManager $tagsManager)
	{
		$this->imagesManager = $imagesManager;
		$this->tagsManager = $tagsManager;
	}

	public function getImages(Machine $machine)
	{
		return $this->imagesManager->findImages($machine);
	}
}

?>
newPOPE
Člen | 648
+
0
-

@artemevsin ono moze sa ti casom stat, ze tych zavislosti ti tam narastie.
Preco nepouzivat Machine ako entity a tie jednotlive Managery si vyziadas az tam kde ich budes potrebovat.

EDIT: aj ta sluzba ktoru popisujes je alternativa

Editoval newPOPE (30. 8. 2016 10:12)

GEpic
Člen | 566
+
0
-

Podle mě by měl být jakýkoliv Manager standalone a poté se ho kdekoliv dotazovat. Než si veškeré managery předávat do mašinky. Co když zjistíš že budeš chtít některého z managerů použít i mimo mašinku, tak i tak si ho budeš muset injectnout někde zvlášť a pak to celé dle mého postrádá smysl.

Přijde mi to jako fitko – že se někteří snaží cvičit super efektivní cviky z NASA, přitom jednoduché věci prostě fungují. #nohate :D

Editoval GEpic (30. 8. 2016 10:32)

artemevsin
Člen | 61
+
0
-

jde mi především o to, že z pohledu byznys logiky aplikace neexistuje zvlášť stroj a zvlášť obrázek, ale existuje stroj, který má obrázky. Takže z tohoto důvodu jsem to chtěl takto zapouzdřit.

Ale čim víc nad tím přemýšlím, tím víc mi to přijde jako zbytečný overhead, přesně jak píšete :D
Takže vás poslechnu a nechám to tak jak to je :)

Oli
Člen | 1215
+
+1
-

jde mi především o to, že z pohledu byznys logiky aplikace neexistuje zvlášť stroj a zvlášť obrázek, ale existuje stroj, který má obrázky. Takže z tohoto důvodu jsem to chtěl takto zapouzdřit.

Tak to jednoduše vynutíš tím, že pokud:

  1. Existuje pouze stroj s obrázkem – tak do konstruktoru entity stroj si předáš obrázek (bez něj nepůjde stroj vytvořit)
  2. Může existovat i stroj bez obrázku (ale ne obrázek bez stroje) – Do konstruktoru obrázku si předáš stroj (bez stroje nepůjde obrázek vytvořit)
  3. Může existovat stroj i obrázek zvlášť – normální setry nebo jakkoli jinak. Prostě nevynucuješ jednu entitu druhou v konstruktoru…
GEpic
Člen | 566
+
0
-

Nebo pokud budete chtít, já vám nějaké obrázky s mašinkama pošlu na mail… :D

artemevsin
Člen | 61
+
0
-

2. Může existovat i stroj bez obrázku (ale ne obrázek bez stroje) – Do konstruktoru obrázku si předáš stroj (bez stroje nepůjde obrázek vytvořit)

Do konstruktoru obrázku se předává objekt implementující IUuidUser, který má metodu getUuid() (což může být stroj, uživatel, firma, prostě cokoliv co má UUID). Takže obrázek ví jen to, že patří nějakému UUID, ale neví, co to je.

Takže pokud chci získat obrázky, musím tomu ImagesManager předat entitu, pro kterou chci vytáhnout obrázky.

Stroj jako takový neví o existenci nějakých obrázků – tj. spravují jen to svoje. Například knihovna, která se stará o stroje, má nějakou interní logiku toho, co všechno ten stroj má, jaké může mít parametry apod.

Knihovna, která spravuje obrázky, stará se jen o ty obrázky, tj. ukládá je do úložiště (přes injectnujtý IStorage), upravuje (opět přes injectnutý IImageProcessor) apod. Díky tomu jsou jednotlivé knihovny znovupoužitelné.

A právě ta vyšší vrstva aplikace by ty jednotlivé knihovny měla spojit, protože až teprve ona ví, že stroj může být s obrázkem. Tudíž mi z toho tak nějak vyplývá, že by se měla vytvořit jakási proxy entita, která zapouzdří tyhle vazby a bude odpovídat byznys logice – tj. že stroj může mít obrázek. Ale pravděpodobně by to jen zbytečně zkomplikovalo vývoj…

newPOPE
Člen | 648
+
0
-

@artemevsin a co pouzit Traity?

artemevsin
Člen | 61
+
0
-

@newPOPE mohl bys to trochu přiblížit? Nenapadá mě, jak by se daly využit…

newPOPE
Člen | 648
+
0
-

Myslel som nieco taketo

class Machine {
  use ImageAwareTrait;
}

trait ImageAwareTrait {
  public function getImages() {
    return $this->imagesManager->findImages($this->getUuid());
  }
}

Akurat je trochu problem tam dodat ten imagesManager :). Ale keby si to napisal nejak takto:

trait ImageAwareTrait {
  /** @var IImagesManager */
  protected $imagesManager;

  public function getImages() {
	if($this->imagesManager) {
      return $this->imagesManager->findImages($this->getUuid());
    }
  }
}

Tak by to mohlo fungovat „automaticky“ stacilo by len injektnut danemu objektu nejaku instanciu IImagesManager.


je to z hlavy cize to moze nefungovat, ide o myslienku skladania.

Editoval newPOPE (30. 8. 2016 15:08)

artemevsin
Člen | 61
+
0
-

jak tak koukám, tak je to skoro stejné jako ten kompozitní objekt :) jen ty jednotlivé funkce jsou zabalené do traity. Ale tomu objektu Machine bych stejně musel předat všechny ty managery…