1
SW_07 • Návrhový vzor: Továrna - Factory 1. Jednoduchá továrna - Simple Factory 2. Tovární metoda - Factory Method 3. Abstraktní továrna - Abstract Factory
2
Vzor Továrna - Factory • Kontext: • při vytváření třídy obyčejně doplníme konstruktory na tvorbu instancí, • Problém: • někdy klient potřebuje nový objekt, ale neví, od které z několika možných tříd (hierarchie) vytvořit instanci, • metoda new není špatná, je ale problémem při změnách – rozšiřování funkčnosti (přidávání dalších podtříd).
3
Vzor Továrna - Factory • Řešení: • vzor Továrna ukládá všechny proměnné aspekty spojené s tvorbou instancí do třídy Továrna nebo jejich podtříd a vytváří tak instance požadovaných tříd. • Klient je „odcloněn“ od tvorby konkrétních instancí. • Rozlišujeme tři formy vzoru Továrna, a to 1. jednoduchá továrna, 2. tovární metoda, 3. abstraktní továrna.
4
Outline Programování proti rozhraní. Kachna kachna = new DivokaKachna();
Kachna kachna; if(piknik) if(piknik) { kachna = new DivokaKachna(); } else if (myslivost) { kachna = new KachniVabnicka();
Existuje řada různých kachen k různému použití.
} else if (koupani) { kachna = new GumovaKachna(); }
Podle toho chceme vytvářet instanci dané kachny.
5
Metoda new • Metoda new (vytvoření instance) je pevně spojena s konkrétní třídou. • Programový návrh by měl být otevřený pro rozšiřování (přidávání dalších tříd), ale také blízký pro modifikaci. • Programování proti rozhraní. • Způsob řešení: – identifikace aspektů, které se mění – jejich oddělení od zbytku „neměnné“ aplikace.
6
Outline Pizza orderPizza() { Pizza pizza;
Identifikace aspektů, které se mění.
Pizza pizza = new Pizza() Pizza(); (); pizza.prepare(); pizza.bake(); pizza.cut(); pizza.box(); return pizza;
Třída Pizza by měla být kvůli flexibilitě rozhraním, nebo abstraktní třídou
}
Nedostatek: potřebujeme více než pouze jeden typ pizzy
7
Outline
Pizza orderPizza(String orderPizza(String type) type) {
Možnost změnit typ pizzy
Pizza pizza = null;
if (type.equals("cheese")) { pizza = new CheesePizza(); } else if (type.equals("pepperoni")) { pizza = new PepperoniPizza(); } else if (type.equals("clam")) { pizza = new ClamPizza(); } else if (type.equals("veggie")) { pizza = new VeggiePizza(); }
část, která se mění další typy … v závislosti na typu se vytváří konkrétní instance pizzy
pizza.prepare(); pizza.bake(); pizza.cut(); pizza.box(); return pizza; }
část, která zůstává stejná
8
Zapouzdření objektu vytváření instancí • Vytvoříme novou třídu, do které uložíme původní část kódu z metody orderPizza(). • Tuto novou třídu nazveme Factory – továrna. • Obecně Factories (továrny)se zabývají detaily vytváření objektů. • Tím máme SimplePizzaFactory – kdykoli chceme další objekt (instanci), požádáme pizza factory o nový objekt.
9
Outline public class SimplePizzaFactory { public Pizza createPizza(String type) { Pizza pizza = null; if (type.equals("cheese")) { pizza = new CheesePizza(); } else if (type.equals("pepperoni")) { pizza = new PepperoniPizza(); } else if (type.equals("clam")) { pizza = new ClamPizza(); } else if (type.equals("veggie")) { pizza = new VeggiePizza(); } return pizza; } }
10
PizzaStore – obchod s pizzou • Obchod s pizzou (PizzaStore) tvoří klientský kód (kód klienta). • Factory (továrna) nám vytvoří požadovanou pizzu.
11
1. Simple Factory
• Simple Factory je spíše idiomem, než návrhovým vzorem.
12 abstract public class Pizza { String name; String dough; String sauce; ArrayList toppings = new ArrayList(); public String getName() { return name; } public void prepare() { System.out.println("Preparing " + name); } public void bake() { System.out.println("Baking " + name); } public void cut() { System.out.println("Cutting " + name); } public void box() { System.out.println("Boxing " + name); } }
Outline
13
Outline public class CheesePizza extends Pizza { public CheesePizza() { name = "Cheese Pizza"; dough = "Regular Crust"; sauce = "Marinara Pizza Sauce"; toppings.add("Fresh Mozzarella"); toppings.add("Parmesan"); } }
Specializovanější třídy třídy Pizza.
14
Outline public class SimplePizzaFactory { public Pizza createPizza(String type) { Pizza pizza = null; if (type.equals("cheese")) { pizza = new CheesePizza(); } else if (type.equals("pepperoni")) { pizza = new PepperoniPizza(); } else if (type.equals("clam")) { pizza = new ClamPizza(); } else if (type.equals("veggie")) { pizza = new VeggiePizza(); } return pizza; } }
Odkaz na pizzu Programování proti rozhraní.
15
Outline public class PizzaStore { SimplePizzaFactory factory;
Klient // konstruktor public PizzaStore(SimplePizzaFactory factory) { this.factory = factory; } public Pizza orderPizza(String type) { Pizza pizza; pizza = factory.createPizza(type); factory.createPizza(type); pizza.prepare(); pizza.bake(); pizza.cut(); pizza.box(); return pizza; } }
odkaz na factory
16
Outline public class PizzaTestDrive {
Aplikace public static void main(String[] args) { SimplePizzaFactory factory = new SimplePizzaFactory(); PizzaStore store = new PizzaStore(factory); Pizza pizza = store.orderPizza("cheese"); System.out.println("We ordered a " + pizza.getName() + "\ "\n"); pizza = store.orderPizza("veggie"); System.out.println("We ordered a " + pizza.getName() + "\ "\n"); } }
Odkaz na factory
17
Jednoduchá továrna • Existuje jen jeden typ továrny, tedy pouze jedna třída Tovarna. • Existuje pouze jeden produkt (pizza), který je dále specifikovaný do různých druhů pizz. • Třída PizzaStore představuje klienta.
Rozšiřování obchodů s pizzou – 2. Factory Method • Potřebujeme mít různé typy továren vytvářející jeden produkt dále specializovaný. • Chceme rozšířit obchody – např. NY, Chicago. • Každý regionální obchod může mít odlišnosti v nabídce stylů pizzy (NY, Chicago, California) – mají odlišné možnosti. • Chceme, aby regionální obchody byly ovlivněny kódem PizzaStore – pizzy jsou připravovány stejným způsobem.
18
19
Možný přístup • Vyjdeme z SimplePizzaFactory a vytvoříme tři odlišné factories: – NYPizzaFactory, – ChicagoPizzaFactory, – CalifirniaPizzaFactory.
20
Outline
NYPizzaFactory nyFactory = new NYPizzaFactory(); PizzaStore nyStore = new PizzaStore(nyFactory PizzaStore(nyFactory); nyFactory);
vytvoření factory pro přípravu NY pizza stylu
nyStore.order(“Cheese”);
ChicagoPizzaFactory chicagoFactory = new ChicagoPizzaFactory(); PizzaStore chicagoStore = new PizzaStore(chicagoFactory PizzaStore(chicagoFactory); chicagoFactory); chicagoStore.order(“Cheese”);
vytvoření PizzaStore a předání odkazu na factory objednání pizzy v NY stylu
21
Zvýšení řízení kvality • Myšlenka SimpleFactory je, že koncesní obchody (franchize) využívají factory k výrobě instancí, ale již např. používají jiné krabice box() a zapomínají krájet pizzu cut(). • Nutnost vytvoření frameworku, který sváže pevněji jednotlivé obchody s pizzou, ale stále zachová pružnost (flexibilitu).
22
Framework pro obchody s pizzou • Existuje cesta, jak soustředit všechny aktivity vytváření pizzy do třídy PizzaStore a nechat frameworku prostor pro regionální styl prodejen. • Postup: – createPizza() přijde zpět do třídy PizzaStore, ale jako abstraktní metoda. – pro regionální styly se vytvoří podtřídy od třídy PizzaStore.
23
Outline public abstract class PizzaStore {
public Pizza orderPizza(String type) { Pizza pizza = createPizza(type); createPizza(type); System.out.println("--System.out.println("--- Making a " + pizza.getName() + " ---"); ---"); pizza.prepare(); pizza.bake(); pizza.cut(); pizza.box(); return pizza; } abstract Pizza createPizza(String item); }
24
Podtřídy mohou rozhodovat public Pizza createPizza(String item) { if (item.equals("cheese")) { return new ChicagoStyleCheesePizza(); } else if (item.equals("veggie")) { return new ChicagoStyleVeggiePizza(); } else if (item.equals("clam")) { return new ChicagoStyleClamPizza(); } else if (item.equals("pepperoni")) { return new ChicagoStylePepperoniPizza(); } else return null; } }
25
Metody createPizza() a orderPizza() • Metoda orderPizza() je definovaná v abstraktní třídě PizzaStore, ale konkrétně je dotvořena v podtřídách. • Metoda orderPizza() volá metodu createPizza(), aby skutečně dostala objekt pizza. • Metoda orderPizza() nemůže vytvořit objekt pizza, protože neví jak. • Když orderPizza() volá createPizza(), jedna z podtříd PizzaStore to provede.
26
Úprava třídy PizzaStore • Všechny podtřídy třídy PizzaStore (regionální obchody) musí doplnit metodu createPizza(), která implementuje jejich (regionální styl) pizzy.
27
Outline public class NYPizzaStore extends PizzaStore { Pizza createPizza(String createPizza(String item) { if (item.equals("cheese")) { return new NYStyleCheesePizza(); } else if (item.equals("veggie")) { return new NYStyleVeggiePizza(); } else if (item.equals("clam")) { return new NYStyleClamPizza(); } else if (item.equals("pepperoni")) { return new NYStylePepperoniPizza(); } else return null; } }
28
Deklarace factory metody (2) public abstract class PizzaStore public Pizza orderPizza(String type) { Pizza pizza; pizza; pizza = createPizza(type); pizza.prepare(); pizza.cut();
pizza.bake();
pizza.box();
return pizza; } protected abstract Pizza createPizza(String item); // abstraktní metoda }
abstract Product factoryMethod(String type)
• podtřídy vytváří konkrétní objekt, • metoda vrací Produkt, který je využit v nadtřídě, • metoda factory izoluje klienta od znalosti, jaký druh konkrétního produktu je vytvářen (instancionován), • metoda factory může mít parametry pro specifikaci vytvářeného produktu.
29
Outline PizzaStore nyPizzaStore = new NYPizzaStore(); NYPizzaStore();
nyPizzaStore.orderPizza(“cheese”);
Pizza pizza = createPizza(“cheese”);
pizza.prepare(); pizza.bake(); pizza.cut(); pizza.box();
Postup při objednávání:
orderPizza() volá createPizza()
30 public abstract class Pizza { String name; String dough; String sauce; ArrayList toppings = new ArrayList(); void prepare() { System.out.println("Preparing " + name); System.out.println("Tossing dough..."); System.out.println("Adding sauce..."); System.out.println("Adding toppings: "); for (int i = 0; i < toppings.size(); i++) { System.out.println(" " + toppings.get(i)); } } void bake() { System.out.println("Bake for 25 minutes at 350"); } void cut() { System.out.println("Cutting the pizza into diagonal slices"); } void box() { System.out.println("Place pizza in official PizzaStore box"); } public String getName() { return name; } }
Outline Musíme doplnit třídu Pizza
31
Outline public class NYStyleCheesePizza extends Pizza { public NYStyleCheesePizza() { name = "NY Style Sauce and Cheese Pizza"; dough = "Thin Crust Dough"; sauce = "Marinara Sauce"; toppings.add("Grated Reggiano Cheese"); } }
podtřída třídy Pizza
32 public class PizzaTestDrive { public static void main(String[] args) { PizzaStore nyStore = new NYPizzaStore(); PizzaStore chicagoStore = new ChicagoPizzaStore(); Pizza pizza = nyStore.orderPizza("cheese"); nyStore.orderPizza("cheese"); System.out.println("Ethan ordered a " + pizza.getName() + "\ "\n"); pizza = chicagoStore.orderPizza("cheese"); chicagoStore.orderPizza("cheese"); System.out.println("Joel ordered a " + pizza.getName() + "\ "\n"); }
} Preparing NY Style Sauce and Cheese Pizza Tossing dough... Adding sauce... Adding toppings: Grated Reggiano Cheese Bake for 25 minutes at 350 Cutting the pizza into diagonal slices Place pizza in official PizzaStore box Ethan ordered a NY Style Sauce and Cheese Pizza Preparing Chicago Style Deep Dish Cheese Pizza Tossing dough... Adding sauce... Adding toppings: Shredded Mozzarella Cheese Bake for 25 minutes at 350 Cutting the pizza into square slices Place pizza in official PizzaStore box Joel ordered a Chicago Style Deep Dish Cheese Pizza
Outline Klient – hlavní program a výpis
33
Vzor Factory (Továrna) • Všechny vzory Factory zapouzdřují vytváření objektu. • Metoda Factory zapouzdřuje vytváření objektu tím, že nechá na podtřídě, aby rozhodla, který objekt vytvoří. • Rozlišujeme třídy pro vytváření – Factory a třídy vlastního produktu - Pizza.
34
Třídy vytváření a třídy produktu
35
Paralelní hierarchie
36
Simple Factory
• Jednoduchá továrna (Simple Factory) a tovární metoda (Factory Method)
37 public abstract class PizzaStore {
Outline
abstract Pizza createPizza(String item); public Pizza orderPizza(String type) { Pizza pizza = createPizza(type); System.out.println("--System.out.println("--- Making a " + pizza.getName() + " ---"); ---"); pizza.prepare(); pizza.bake(); pizza.cut(); pizza.box(); return pizza; } }
PizzaStore je abstraktní třída s abstraktní metodou createPizza()
38 public class NYPizzaStore extends PizzaStore { Pizza createPizza(String item) { if (item.equals("cheese")) { return new NYStyleCheesePizza(); } else if (item.equals("veggie")) { return new NYStyleVeggiePizza(); } else if (item.equals("clam")) { return new NYStyleClamPizza(); } else if (item.equals("pepperoni")) { return new NYStylePepperoniPizza(); } else return null; } }
Outline
39 public class PizzaTestDrive { public static void main(String[] args) { PizzaStore nyStore = new NYPizzaStore(); PizzaStore chicagoStore = new ChicagoPizzaStore(); Pizza pizza = nyStore.orderPizza("cheese"); System.out.println("Ethan ordered a " + pizza.getName() + "\ "\n"); pizza = chicagoStore.orderPizza("cheese"); System.out.println("Joel ordered a " + pizza.getName() + "\ "\n"); pizza = nyStore.orderPizza("clam"); System.out.println("Ethan ordered a " + pizza.getName() + "\ "\n"); pizza = chicagoStore.orderPizza("clam"); System.out.println("Joel ordered a " + pizza.getName() + "\ "\n"); } }
Outline
Definice vzoru tovární metoda Factory method • Návrhový vzor tovární metoda (factory method) definuje rozhraní pro vytvoření objektu, ale nechává na podtřídách implementujících toto rozhraní aby rozhodly, která třída vytvoří objekt (instanci). • Tovární metoda tedy odkládá proces instanciace na podtřídu.
40
Tovární metoda Factory Method
• Product = Pizza; ConcreteProduct = NYStyleCheesePizza; • Creator = PizzaStore; factoryMethod() = createPizza(); ConcreteCreator = NYPizzaStore factoryMethod() = createPizza();
41
42
Factory Method – tovární metoda • Tovární metoda je výhodná pokud existuje jeden konkrétní tvůrce (kreátor), protože implementace produktu je oddělena od jeho použití. • Pokud přidáte další produkty, nebo změníte implementaci produktu, kreátor to neovlivní. • Tovární metodu jsme implementovali jako parametrickou (parametr ovlivňuje typ instance). • Tovární metoda však může vytvářet pouze jeden typ instance.
43
Tovární metoda • K omezení chyb při předávání parametru (projeví se jako run time error) je možné místo typu (String) využívat enum (výčtový typ). • Simple factory nemá podtřídy na rozdíl od factory method.
44
Tovární metoda
• Řešení: • Tovární metoda umožňuje tvůrci tříd definovat rozhraní pro vytvářený objekt, zatímco si ponechává volbu, ze které třídy instanci vytvoří.
45
Klasický příklad použití: Iterátor • Vzor Iterátor poskytuje způsob zpřístupnit sekvenčně prvky kolekce • Při vytváření instancí Iterátorů je použita metoda Factory • V rozhraní Collection je metoda iterator(), kterou pak implementují všechny třídy kolekcí. Metoda iterator() izoluje toho, kdo ji volá od povědomí, od které vlastně třídy vytváří instanci.
46 public class ShowIterator { public static void main(String[] args) { List list = Arrays.asList( new String[] { "fountain", "rocket", "sparkler" }); Iterator iter = list.iterator(); while (iter.hasNext()) System.out.println(iter.next()); // Uncomment the next line to see the iterator's actual class: // System.out.println(iter.getClass().getName()); } }
Outline
47
Rozpoznání Metody Factory • mylná domněnka, že každá metoda která vytváří a vrací objekt je metoda „Factory“, • v OOP je běžné, že metody vrací nový objekt, • fakt, že metoda vytváří nový objekt nemusí znamenat, že je příkladem metody Factory, • metoda Factory je metoda, která jednak vytváří nový objekt a dále izoluje klienta od povědomí, od které třídy vytváří objekt, • při požadavku na objekt, je definitivní třída objektu, který bude vytvořen závislá na chování objektu Factory, který dostal požadavek na vytvoření nového objektu.
48
Problémy návrhu – další rozšiřování • potřebujeme zavést další třídu CaliforniaStylePizzaStore.
49
Závislosti a inverzní princip závislosti • Pro vysvětlení se posuneme zpět; neznáme OO factories a vše řešíme v jednom objektu.
50 public class DependentPizzaStore { public Pizza createPizza(String style, String type) { Pizza pizza = null; if (style.equals("NY")) { if (type.equals("cheese")) { pizza = new NYStyleCheesePizza(); } else if (type.equals("veggie")) { pizza = new NYStyleVeggiePizza(); } else if (type.equals("clam")) { pizza = new NYStyleClamPizza(); } else if (type.equals("pepperoni")) { pizza = new NYStylePepperoniPizza(); } } else if (style.equals("Chicago")) { if (type.equals("cheese")) { pizza = new ChicagoStyleCheesePizza(); } else if (type.equals("veggie")) { pizza = new ChicagoStyleVeggiePizza(); } else if (type.equals("clam")) { pizza = new ChicagoStyleClamPizza(); } else if (type.equals("pepperoni")) { pizza = new ChicagoStylePepperoniPizza(); }
Outline
51
Outline
} else { System.out.println("Error: invalid type of pizza"); return null; } pizza.prepare(); pizza.bake(); pizza.cut(); pizza.box(); return pizza; } }
52
Závislosti objektů
• Všechny objekty se vytváří v rámci třídy PizzaStore, místo delegování na factory. • Každý nový druh pizzy, vytváří další závislost na PizzaStore. • Každá změna konkrétní implementace pizzy, ovlivňuje PizzaStore. • PizzaStore závisí na implementaci Pizzy.
Princip inverzní závislosti (The Dependency Inversion Principle)
• Obecně snížení závislostí konkrétních tříd v programovém kódu je „záslužná věc“. • Princip návrhu (Design Principle): – Buď závislý na abstrakcích. Nebuď závislý na konkrétních třídách.
• Je to podobné jako: – Programování k rozhraní, ne k implementaci.
53
Princip inverzní závislosti (The Dependency Inversion Principle)
• Princip inverzní závislosti však vytváří silnější tvrzení o abstrakci: – Předpokládá, že naše vysoko úrovňové komponenty by neměly být závislé na nízko úrovňových komponentách; obojí by měly spíše záviset na abstrakcích (rozhraní, abstraktní třídy).
• Vysoko úrovňová komponenta: – je třída, s chováním definovaným pomocí nízko úrovňových komponent. – Např. PizzaStore – vysoko úrovňová komponenta; její chování je definované v pojmech pizza (vytváří všechny druhy pizz)
54
Princip inverzní závislosti (The Dependency Inversion Principle)
• Třída PizzaStore je závislá na konkrétních třídách pizza (nízko úrovňové komponenty). • Princip říká, být závislý na abstrakcích, jak pro vysokoúrovňové, tak nízko úrovňové komponenty (high level components, low level components).
55
Princip inverzní závislosti (The Dependency Inversion Principle) • Pizza bude abstraktní třída. • PizzaStore („high level component“) nyní závisí na abstraktní třídě. • Konkrétní podtřídy od třídy Pizza („low level components“) jsou závislé na abstraktní třídě Pizza také. • Metoda factory není jedinou technikou, která se řídí inverzním principem závislosti, ale je jedna z výkonných technik.
56
Princip inverzní závislosti (The Dependency Inversion Principle)
• Inverzní princip je zde proto, protože obracíme typický způsob myšlení o OO návrhu. • Nízko-úrovňové komponenty závisí na abstrakci vyšší úrovně. • Vysoko-úrovňové komponenty jsou svázány se stejnou abstrakcí. • Závislost top-bottom se změnila. • Komponenty high-level a low-level jsou závislé na stejné úrovni abstrakce.
57
58
Jak nezapomenout na princip • O tvorbu konkrétních objektů třídy Pizza se bude starat factory. • Žádná proměnná by neměla odkazovat na konkrétní třídu. – použití new vede k odkazu na konkrétní třídu, je třeba použít factory,
• Žádná třída by se neměla odvozovat od konkrétní třídy. – pokud vytváříte podtřídu, jste závislý na nadtřídě; je lépe vytvářet podtřídu od abstraktní třídy nebo rozhraní.
59
Jak nezapomenout na princip • Žádná metoda by neměla předeklarovat (overrride) implementovanou metodu základních tříd. – myslí se tím metodu abstraktní třídy (např. Pizza).
• Je to jen pomůcka, kterou ne vždy můžeme dodržet.
Rozšiřování návrhu – 3. Abstract Factory • V původním návrhu – třída Pizza obsahovala pouze jeden typ surovin - produktu (těsto, omáčku, sýr…). • Tyto základní suroviny se však rozšiřují a specializují. • Vzniká rodina ingrediencí. • Specializace továren zůstává a přidává se více produktů.
60
61 public interface PizzaIngredientFactory { public public public public public public }
Dough createDough(); Sauce createSauce(); Cheese createCheese(); Veggies[] createVeggies(); Pepperoni createPepperoni(); Clams createClam();
Outline Vytváření továren pro ingredience – Ingredient factories. Pro každou ingredienci vytváříme konkrétní metodu.
62 public class NYPizzaIngredientFactory implements PizzaIngredientFactory { public Dough createDough() { return new ThinCrustDough(); } public Sauce createSauce() { return new MarinaraSauce(); } public Cheese createCheese() { return new ReggianoCheese(); } public Veggies[] createVeggies() { Veggies veggies[] = { new Garlic(), new Onion(), new Mushroom(), new RedPepper() }; return veggies; } public Pepperoni createPepperoni() { return new SlicedPepperoni(); } public Clams createClam() { return new FreshClams(); } }
Outline Vytvoříme factory (továrnu) pro každý region.
63
Úpravy třídy Pizza • Máme vytvořené factories pro každý region, které dodávají ingredience podle regionu. • Nutné úpravy ve třídě Pizza:
64 public abstract class Pizza { String name; Dough dough; Sauce sauce; Veggies veggies[]; Cheese cheese; Pepperoni pepperoni; Clams clam; abstract void prepare(); void bake() { System.out.println("Bake for 25 minutes at 350"); } void cut() { System.out.println("Cutting the pizza into diagonal” diagonal” + “slices"); } void box() { System.out.println("Place pizza in” in” + “official PizzaStore box"); } void setName(String name) { this.name = name; } String getName() { return name; } public String toString() { // kód který tiskne pizzu } }
Outline
65
Úprava podtříd třídy Pizza • Dopracovat použití regionálních ingrediencí. • Podtřídy využívají factory k vytváření ingrediencí.
sauce = ingredientFactory.createSauce();
66
Outline public class CheesePizza extends Pizza { PizzaIngredientFactory ingredientFactory; // konktruktor doplňuje odkaz na ingredientFactory public CheesePizza(PizzaIngredientFactory ingredientFactory) { this.ingredientFactory = ingredientFactory; } void prepare() { System.out.println("Preparing " + name); dough = ingredientFactory.createDough(); ingredientFactory.createDough(); sauce = ingredientFactory.createSauce(); cheese = ingredientFactory.createCheese(); } }
Podobné je to i u dalších tříd.
67
Úprava třídy PizzaStore • Potřebujeme přidat reference na jejich lokální továrny ingrediencí.
68 public class NYPizzaStore extends PizzaStore { protected Pizza createPizza(String createPizza(String item) { Pizza pizza = null; PizzaIngredientFactory ingredientFactory = new NYPizzaIngredientFactory(); if (item.equals("cheese")) { pizza = new CheesePizza(ingredientFactory ingredientFactory); CheesePizza(ingredientFactory); pizza.setName("New York Style Cheese Pizza"); } else if (item.equals("veggie")) { pizza = new VeggiePizza(ingredientFactory VeggiePizza(ingredientFactory); ingredientFactory); pizza.setName("New York Style Veggie Pizza"); } else if (item.equals("clam")) { pizza = new ClamPizza(ingredientFactory ClamPizza(ingredientFactory); ingredientFactory); pizza.setName("New York Style Clam Pizza"); } else if (item.equals("pepperoni")) { pizza = new PepperoniPizza(ingredientFactory ingredientFactory); PepperoniPizza(ingredientFactory); pizza.setName("New York Style Pepperoni Pizza"); } return pizza; } }
Outline Metoda createPizza() má nastavenou lokální proměnnou ingredientFactory na svoji regionální ingredient factory. Tato proměnná se předává každé pizze aby si mohla vytvořit ingredience.
69
Továrny - Factories • Abstraktní továrna poskytuje rozhraní pro skupinu (rodinu) různých produktů. • Od abstraktní továrny jsou odvozeny konkrétní továrny, které produkují stejné produkty s jinou implementací.
70
Outline 1. Potřebujeme PizzaStore např. NY PizzaStore, PizzaStore nyPizzaStore = new PizzaStore(); PizzaStore(); 2. uděláme objednávku, nyPizzaStore.orderPizza(“cheese”); 3. metoda orderPizza() nejdříve volá metodu createPizza(), Pizza pizza = createPizza(“cheese”); 4. když je vyvolaná metoda createPizza() tehdy je začleněna ingredient
factory, Pizza = pizza new CheesePizza(nyIngredientFactory); 5. potřebujeme připravit pizzu; v okamžiku kdy je zavolána metoda prepare(), je požádána factory k přípravě ingrediencí, void prepare() { dough = factory.createDough(); sauce = factory.createSauce(); cheese = factory.createCheese(); } 6. konečně je pizza připravena a metoda orderPizza() volá další metody
bake(), cut(), boxes().
Postup objednání a přípravy pizzy
Vzor 3. Abstract Factory Abstraktní továrna
• Vzor abstraktní továrna poskytuje rozhraní pro vytváření řady příbuzných nebo závislých objektů bez specifikace jejich konkrétní třídy. • Abstraktní továrna dovoluje klientovi používat „abstraktní rozhraní“ k vytváření množiny příbuzných produktů bez znalosti konkrétních produktů, které jsou vytvářeny. • Tímto způsobem je klient oddělen od specifikací konkrétních produktů.
71
72
Abstraktní továrna Client
«interface» AbstractProductA
«interface» AbstractFactory createProductA() createProductB()
ConcreteFactory1 createProductA() createProductB()
ProductA2
ProductA1
ConcreteFactory2 «interface» AbstractProductB
createProductA() createProductB()
ProductB2
ProductB1
73
Abstraktní továrna • Klient je napsán „oproti“ abstraktní továrně, ale za běhu programu je to doplněno na konkrétní továrnu. • Konkrétní továrny implementují různé rodiny produktů. Klient využívá pouze jednu konkrétní továrnu v daném čase. • Každá konkrétní továrna může produkovat celou množinu příbuzných produktů.
74
Abstraktní továrna Pizza prepare() // other methods
«interface» Dough «interface» PizzaIngredientFactory ThickCrustDough
ThinCrustDough
createDough() createSauce() createCheese() CreateClams() «interface» Sauce
PlumTomatoSauce NYPizzaIngredientFactory createDough() createSauce() createCheese() CreateClams()
MarinaraSauce
ChicagoPizzaIngredientFactory createDough() createSauce() createCheese() CreateClams()
«interface» Cheese
MozzarellaCheese
RegglanoCheese
«interface» Clams
FrozenClams
FreshClams
75 public class PizzaTestDrive { public static void main(String[] args) { PizzaStore nyStore = new NYPizzaStore(); PizzaStore chicagoStore = new ChicagoPizzaStore(); Pizza pizza = nyStore.orderPizza("cheese"); System.out.println("Ethan ordered a " + pizza + "\ "\n"); pizza = chicagoStore.orderPizza("cheese"); System.out.println("Joel ordered a " + pizza + "\ "\n"); pizza = nyStore.orderPizza("clam"); System.out.println("Ethan ordered a " + pizza + "\ "\n"); pizza = chicagoStore.orderPizza("clam"); System.out.println("Joel ordered a " + pizza + "\ "\n"); } }
Outline
76 public abstract class PizzaStore { protected abstract Pizza createPizza(String item); public Pizza orderPizza(String type) { Pizza pizza = createPizza(type); System.out.println("--System.out.println("--- Making a " + pizza.getName() + " ---"); ---"); pizza.prepare(); pizza.bake(); pizza.cut(); pizza.box(); return pizza; } }
Outline
77 public class NYPizzaStore extends PizzaStore { protected Pizza createPizza(String item) { Pizza pizza = null; PizzaIngredientFactory ingredientFactory = new NYPizzaIngredientFactory(); if (item.equals("cheese")) { pizza = new CheesePizza(ingredientFactory CheesePizza(ingredientFactory); ingredientFactory); pizza.setName("New York Style Cheese Pizza"); } else if (item.equals("veggie")) { pizza = new VeggiePizza(ingredientFactory VeggiePizza(ingredientFactory); ingredientFactory); pizza.setName("New York Style Veggie Pizza"); } return pizza; } }
Outline
78 public interface PizzaIngredientFactory { public public public public public public }
Dough createDough(); Sauce createSauce(); Cheese createCheese(); Veggies[] createVeggies(); Pepperoni createPepperoni(); Clams createClam();
Outline
79 public class NYPizzaIngredientFactory implements PizzaIngredientFactory { public Dough createDough() { return new ThinCrustDough(); } public Sauce createSauce() { return new MarinaraSauce(); } public Cheese createCheese() { return new ReggianoCheese(); } public Veggies[] createVeggies() { Veggies veggies[] = { new Garlic(), new Onion(), new Mushroom(), new RedPepper() }; return veggies; } public Pepperoni createPepperoni() { return new SlicedPepperoni(); } public Clams createClam() { return new FreshClams(); } }
Outline
80 public abstract class Pizza { String name; Dough dough; Sauce sauce; Veggies veggies[]; Cheese cheese; Pepperoni pepperoni; Clams clam; abstract void prepare(); void bake() { System.out.println("Bake for 25 minutes at 350"); } void cut() { System.out.println("Cutting the pizza into diagonal slices"); } . . . }
Outline
81 public class PepperoniPizza extends Pizza { PizzaIngredientFactory ingredientFactory; ingredientFactory; public PepperoniPizza(PizzaIngredientFactory ingredientFactory) { this.ingredientFactory = ingredientFactory; } void prepare() { System.out.println("Preparing " + name); dough = ingredientFactory.createDough(); sauce = ingredientFactory.createSauce(); cheese = ingredientFactory.createCheese(); veggies = ingredientFactory.createVeggies(); pepperoni = ingredientFactory.createPepperoni(); } }
Outline
82 public interface Dough { public String toString(); }
public class ThickCrustDough implements Dough { public String toString() { return "ThickCrust style extra thick crust dough"; } }
Outline
83
Jiný příklad Abstraktní továrny GameEnvironment -gameElementFactory -player -obstacle +play()
«rozhraní» Obstacle +action() «rozhraní» GameElementFactory +makePlayer() +makeObstacle() Puzzle
NastyWeapons
+action() KillAndDismember
KittiesAndPuzzles
+makePlayer() +makeObstacle()
+makePlayer() +makeObstacle()
+action()
«rozhraní» Player +interactWith()
Kitty
KungFuGuy
+interactWith()
+interactWith()
84
Jiný příklad Abstraktní továrny • Produkty, které se vytváří: Obstacle, Player. • Továrny: KillAndDismember, KitiesAndPuzzles. • Klient - GameEnvironment
85 public interface Obstacle { void action(); } public class Puzzle implements Obstacle { @Override public void action() { System.out.println("Puzzle"); } } public class NastyWeapon implements Obstacle { @Override public void action() { System.out.println("NastyWeapon"); } }
Outline
86 public interface Player { void interactWith(Obstacle o); } public class KungFuGuy implements Player { @Override public void interactWith(Obstacle obstacle) { System.out.print("KungFuGuy now battles a "); obstacle.action(); } } public class Kitty implements Player { @Override public void interactWith(Obstacle obstacle) { System.out.print("Kitty has encountered a "); obstacle.action(); } }
Outline
87 public interface GameElementFactory { Player makePlayer(); Obstacle makeObstacle(); }
Outline
88 public class KittiesAndPuzzles implements GameElementFactory { @Override public Player makePlayer() { return new Kitty(); } @Override public Obstacle makeObstacle() { return new Puzzle(); } }
Outline
89 public class KillAndDismember implements GameElementFactory { @Override public Player makePlayer() { return new KungFuGuy(); } @Override public Obstacle makeObstacle() { return new NastyWeapon(); } }
Outline
90 public class GameEnvironment { // tovarna private GameElementFactory factory; // produkty private Player player; private Obstacle obstacle; // konstruktor public GameEnvironment( GameElementFactory factory) { this.factory = factory; player = factory.makePlayer(); obstacle = factory.makeObstacle(); } public void play() { player.interactWith(obstacle); } }
Outline
91 public class Games { public static void main(String[] args) { // ruzne tovarny GameElementFactory kp = new KittiesAndPuzzles(), kd = new KillAndDismember(); // ruzne prostredi GameEnvironment g1 = new GameEnvironment(kp), g2 = new GameEnvironment(kd); g1.play(); g2.play(); } }
Outline
92
Abstraktní továrna • • • •
Simple factory Factory method Abstract factory. Metody Abstract Factory jsou implementovány jako factory metody. • Vzor abstract factory vytváří rodinu příbuzných produktů.