Design Patterns: Factory vs Strategy
- Klobás
- Člen | 113
Ahoj opět.
Předně se omlouvám, vím, že to na Nette fórum nepatří, ale přijde
mi, že se tu shlukují Ti nejlepší z nejlepších.
Pokud to někomu vadí, tak už sem tento typ dotazu psát nebudu.
Studuji Design Patterns, mám k tomu tyhle 3 zdroje
- https://refactoring.guru/design-patterns
- https://designpatternsphp.readthedocs.io/
- https://github.com/…patterns-php
Některé návrhové vzory jsou jasné od začátku, ale některé mi dělají bolehlav (viz včerejší Facade vs Repository, díky @Mabar za vysvětlení).
Ted jsem zase na vážkách v tomto duelu.
Factory nemusí být jen statická, je těch vzorů několik. Prošel jsem si
to a myslel jsem, že chápu.
Dívám se na Strategy a zrovna tento příklad mě dostal zase na hranici
pochybností.
Ukázka kódu jak je to špatně a jak se to má přetavit ve Strategy
vzor:
Nicméně mi přijde, že tohle je spíše Factory. A obecně mi přijde, že
Factory Method nebo jak se tomu říká, je na hranici Strategy.
Řekne mi k tomu někdo někdo víc? Nějaký jednoduchý příklad, aby to šlo rozpoznat kdy Factory (ne základní statická) a kdy Strategy?
<?php
Interface Product_Type {
public function get_all();
//skipped other function definitions to keep this example simple
}
Class Shoe implements Product_Type {
public function get_all() { return DB::table('shoes')->all(); }
}
Class Apparel implements Product_Type {
public function get_all() { return DB::table('apparels')->all(); }
}
Class Mobiles implements Product_Type {
public function get_all() { return DB::table('mobiles')->all(); }
}
// STD USAGE - BAD USAGE
if( $product_type == 'shoes' ) {
$product_obj = new Shoe();
}
elseif( $product_type == 'apparels' ) {
$product_obj = new Shoe();
}
elseif( $product_type == 'mobiles' ) {
$product_obj = new Mobile();
}
$all_products = $product_obj->get_all();
// GOOD USAGE
Class Listing {
private $product_type;
public function set_product_type($product_type) {
if( $product_type == 'shoes' ) {
$this->product_type = new Shoe();
}
elseif( $product_type == 'apparels' ) {
$this->product_type = new Apparels();
}
elseif( $product_type == 'mobiles' ) {
$this->product_type = new Mobile();
}
}
public function get_all() {
return $this->product_type->get_all();
}
}
$listing_obj = new Listing();
$listing_obj->set_product_type($product_type);
$all_products = $listing_obj->get_all();
?>
Editoval Klobás (18. 3. 2020 9:19)
- Marek Bartoš
- Nette Blogger | 1280
Imho moc nemá smysl se snažit cíleně používat design patterny. Když nevíš který použít, tak kód napiš tak jak ti to přijde správné a teprve potom se snaž najít vzor, který ti ho pomůže zjednodušit.
Rozdíl mezi prvním a druhým případem je, že ten druhý skrývá implementační detaily, zatímco v tom prvním o nich výš. Pro znovupoužitelnost je tedy určitě lepší ten druhý.
Strategy pattern se často využívá na kolekce tříd, ke kterým lze přistupovat stejně, ale používají se pro různé vstupy. Například když budu chtít načítat data ze souborů na základě koncovky souboru, tak může jednoduchá implementace vypadat takto:
class LazyFileReaderManager
{
/** @var array<string, string> */
private array $readers = [
'neon' => NeonFileReader::class,
'xml' => XmlFileReader::class,
'yaml' => YamlFileReader::class,
];
/** @var array<string, FileReader> */
private array $instances = [];
public function get(string $suffix): FileReader
{
if (isset($this->instances[$suffix])) {
return $this->instances[$suffix];
}
if (!isset($this->readers[$suffix])) {
throw new InvalidArgumentException(sprintf(
'File reader for suffix `%s` does not exist.',
$suffix
));
}
return $this->instances[$suffix] = new $suffix();
}
public function add(string $suffix, FileReader $reader): void
{
$this->instances[$suffix] = $reader;
}
}
Jako factory ti může strategy pattern přijít díky tomu, že v tvém případě vytváří nové objekty, ale to není pravidlo. Do svého manageru můžeš vkládat i již vytvořené objekty nebo si je může manager vyžádat z DI (v takovém případě manager používá DIC jako svou závislost a v add() metodě přijímá místo objektu název služby)
Též příklad s produkty eshopu není vhodný, jde o chybné využití strategy patternu. Minimum eshopů má takto nízký počet produktů (ty si s takovým přístupem vystačí a může být jednodušší na naprogramování, ale správně není), správnější řešení by bylo mít třídu Product a rozdíly mezi typy produktů si řešit v něm, například pomocí ProductAttribute, bez podmínkování jednotlivých typů.
Editoval Mabar (18. 3. 2020 10:17)
- Klobás
- Člen | 113
Jako factory ti může strategy pattern přijít díky tomu, že v tvém případě vytváří nové objekty, ale to není pravidlo. Do svého manageru můžeš vkládat i již vytvořené objekty nebo si je může manager vyžádat z DI (v takovém případě manager používá DIC jako svou závislost a v add() metodě přijímá místo objektu název služby)
Super, díky této informaci mi to přijde mnohem jasnější!
Editoval Klobás (18. 3. 2020 10:29)