75
Factory Method and Abstract Factory Pattern

Pat’s Pizza creation Pizza orderPizza(String type){ Pizza pizza; if (type.equals(“cheese”)) { pizza = new CheesePizza(); } else if type.equals(“greek”))

Embed Size (px)

DESCRIPTION

Identifying the aspects that vary If the pizza shop decides to change the types of pizza it offers, the orderPizza method has to be changed.

Citation preview

Pats Pizza creation Pizza orderPizza(String type){ Pizza pizza; if (type.equals(cheese)) { pizza = new CheesePizza(); } else if type.equals(greek)) { pizza = new GreekPizza(); } else if type.equals(pepperoni)) { pizza = new PepperoniPizza(); } pizza.prepare(); pizza.bake(); pizza.cut(); pizza.box() return pizza; } Identifying the aspects that vary If the pizza shop decides to change the types of pizza it offers, the orderPizza method has to be changed. Pizza orderPizza(String type){ Pizza pizza; if (type.equals(cheese)) { pizza = new CheesePizza(); } else if type.equals(greek)) { pizza = new GreekPizza(); } else if type.equals(pepperoni)) { pizza = new PepperoniPizza(); } pizza.prepare(); pizza.bake(); pizza.cut(); pizza.box() } Part that varies. Part that remains constant Encapsulating object creation if (type.equals(cheese)) { pizza = new CheesePizza(); } else if type.equals(greek)) { pizza = new GreekPizza(); } else if type.equals(pepperoni)) { pizza = new PepperoniPizza(); } SimplePizzaFactory Building a simple pizza factory 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; } Reworking the PizzaStore Class public class PizzaStore { SimplePizzaFactory factory; public PizzaStore(SimplePizzaFactory factory) { this.factory = factory; } public Pizza orderPizza(String type) { Pizza pizza; pizza = factory.createPizza(type); pizza.prepare(); pizza.bake(); pizza.cut(); pizza.box(); return pizza; } Complete example for Simple Factory SimplePizzaFactory factory = new SimplePizzaFactory(); PizzaStore store = new PizzaStore(factory); Pizza pizza = store.orderPizza("cheese"); Simple Factory Defined PizzaStore orderPizza() SimplePizzaFactory createPizza() Pizza prepare() bake() cut() box() CheesePizza VeggiePizza ClamPizza Creating multiple factories PizzaStore NYPizzaFactory ChicagoPizzaFactory Allowing the subclasses to decide SimplePizzaFactory createPizza() NYStylePizzaFactory createPizza() ChicagoStylePizzaFactory createPizza() A factory method handles object creation and encapsulates it in the subclass. This decouples the client code in the super class from the object creation that happens in the subclass. class NYSimpleFactory extends SimpleFactory{ Pizza createPizza(String type){//creates Newyork specific Pizza pizza;... return pizza; } class PizzaStore { SimpleFactory factory; public PizzaStore(SimpleFactory fact){ factory = fact; } Pizza orderPizza(String type){ Pizza p = factory.createPizza(type); p.prepare(); p.bake(); p.cut(); p.box(); } Creating multiple instances NYPizzaFactory nyFactory = new NYPizzaFactory(); PizzaStore nyStore = new PizzaStore(nyFactory); Pizza pizza = nyStore.orderPizza("cheese"); ChicagoPizzaFactory chicagoFactory = new ChicagoPizzaFactory(); PizzaStore chicagoStore = new PizzaStore(chicagoFactory); Pizza pizza = chicagoStore.orderPizza("cheese"); Simple Factory Pizza prepare() bake() cut() box() CheesePizza VeggiePizza PizzaStore Factory: SimplyPizzaFactory orderPizza() SimplePizzaFactory createPizza() NYStylePizzaFactory createPizza() ChicagoStylePizzaFactory createPizza() Allowing the subclasses to decide PizzaStore createPizza() orderPizza() NYStylePizzaStore createPizza() ChicagoStylePizzaStore createPizza() A factory method handles object creation and encapsulates it in the subclass. This decouples the client code in the super class from the object creation that happens in the subclass. Alternate approach Abstract method a framework for the pizza store public abstract class PizzaStore { abstract Pizza createPizza(String item); public Pizza orderPizza(String type) { Pizza pizza = createPizza(type); pizza.prepare(); pizza.bake(); pizza.cut(); pizza.box(); return pizza; } class NYPizzaStore extends PizzaStore{ Pizza createPizza(String type){..... return pizza; } Creating multiple instances PizzaStore nyStore = new NYPizzaStore(); Pizza pizza = nyStore.orderPizza("cheese"); PizzaStore chicagoStore = new ChicagoPizzaStore(); Pizza pizza = chicagoStore.orderPizza("cheese"); Factory Method Pattern The factory method pattern defines an interface for creating an object, but lets subclasses decide which class to instantiate. Factory method lets a class defer instantiation to subclass. Factory Method Pattern UML PizzaStore Factory Method Pattern PizzaStore createPizza() orderPizza() NYStylePizzaStore createPizza() ChicagoStylePizzaStore createPizza() Pizza Prepare() Bake() Cut() Box() NYStyleCheesePizza ChStyleCheesePizza Creator Classes Product Classes orderPizza(){ Pizz p=createPizza(); P.prepare(); p.bake(); p.cut(); p.box(); } Looking at object dependencies Pizza Store NyStyle Cheeze Pizza NyStyle Cheeze Pizza NyStyle Cheeze Pizza NyStyle Clam Pizza Chicago Cheeze Pizza Chicago Cheeze Pizza Chicago Cheeze Pizza Chicago Clam Pizza Design Principle Dependency Inversion Principle Depend upon abstractions. Do not depend upon concrete classes. High level modules should not depend upon low level modules. Both should depend upon abstractions. Abstractions should not depend upon details. Details should depend upon abstractions. [The Dependency Inversion Principle has been proposed by Robert C. Martin] Applying the principle Pizza Store NyStyle Cheeze Pizza NyStyle Cheeze Pizza NyStyle Cheeze Pizza NyStyle Clam Pizza Chicago Cheeze Pizza Chicago Cheeze Pizza Chicago Cheeze Pizza Chicago Clam Pizza Pizza is an abstract class Some guidelines to help with the principle Try and avoid having variables that refer to a concrete class Try and avoid deriving from a concrete class Try and avoid overriding an implemented method Extending the factory pattern Expanding the Pizza store example How do we deal with families of ingredients? Chicago: FrozenClams, PlumTomatoSauce, ThickCrustDough, MozzarellaCheese New York: FreshClams, MarinaroSauce, ThinCrustDough, ReggianoCheese California: Calamari, BruuuschettaSauce, VeryThinCrust, GoatCheese Building the ingredient factories public interface PizzaIngredientFactory { public Dough createDough(); public Sauce createSauce(); public Cheese createCheese(); public Veggies[] createVeggies(); public Pepperoni createPepperoni(); public Clams createClam(); } Building NY ingredient factory 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(); } Reworking the pizzas 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"); } void box() { System.out.println("Place pizza in official PizzaStore box"); } void setName(String name) { this.name = name; } String getName() { return name; } public String toString() { \\ code to print pizza here } Abstract Factory Pattern defined The abstract factory pattern provides an interface for creating families of related or dependent objects without specifying their concrete classes. Motivation Consider a composite class which must contain the same members but be created differently depending on the application. The idea behind abstract factory is that you can pass a class (the factory) to the constructor which has a standard interface. This interface is then used by the constructor, but the methods it uses are implemented differently for each kind of factory. Abstract Factory Concrete Factory Abstract Product Concrete Product ProductA1 Abstract Factory Pattern > AbstractFactory CreateProductA() CreateProductB() ConcreteFactory1 CreateProductA() CreateProductB() ConcreteFactory2 CreateProductA() CreateProductB() Client > AbstractProdcutA ProductA1 > AbstractProdcutB ConcreteFactory1 CreateProductA() CreateProductB() ProductB1 ProductA2 ProductB2 Real world scenarios Windows XP demonstrates the Abstract Factory pattern in its ability to instantiate families of Windows Forms elements. Lets say you are an online bookstore and people come to your website to buy books. When a customer orders a book, you just have another book distributor send the book directly to the customer. You are a middle man and you dont stock the books. You have many distributors that you can choose to send the books directly to your customers. On-line book order If the customer is in the east coast, you will use EastCoastDistributor If the customer is in the mid-west, you will use MidWestDistributor If the customer is in the west coast, you will use WestCoastDistributor Client code: //the client gets the distributor without having //to know which distributor is being used IDistributor b = bookStore.GetDistributor(); Taking another step further, we can abstract out the BookStore as an interface and have more types of bookstores, as shown below: IBookStore interface //the factory public interface IBookStore { IDistributor GetDistributor(); } //concrete factory public class BookStoreA : IBookStore { private CustomerLocation location; public BookStoreA(CustomerLocation location) { this.location = location; } IDistributor IBookStore.GetDistributor() { //internal logic on which distributor to return //*** logic can be changed without changing the client code **** switch (location) { case CustomerLocation.EastCoast: return new EastCoastDistributor(); case CustomerLocation.MidWest: return new MidWestDistributor(); case CustomerLocation.WestCoast: return new WestCoastDistributor(); } return null; } IBookStore interface //the product public interface IDistributor { void ShipBook(); } //concrete product public class EastCoastDistributor : IDistributor { void IDistributor.ShipBook() { Console.WriteLine("Book shipped by East Coast Distributor"); } //concrete product public class MidWestDistributor : IDistributor { void IDistributor.ShipBook() { Console.WriteLine("Book shipped by Mid West Distributor"); } //conceret product public class WestCoastDistributor : IDistributor { void IDistributor.ShipBook() { Console.WriteLine("Book shipped by West Coast Distributor"); } public enum CustomerLocation { EastCoast, MidWest, WestCoast } class Program { static void Main(string[] args) { Console.WriteLine("East Coast Customer:"); IBookStore bookstore = new BookStoreA(CustomerLocation.EastCoast); ShipBook(bookstore); Console.WriteLine("Mid West Customer:"); bookstore = new BookStoreA(CustomerLocation.MidWest); ShipBook(bookstore); Console.WriteLine("West Coast Customer:"); bookstore = new BookStoreA(CustomerLocation.WestCoast); ShipBook(bookstore); } private static void ShipBook(IBookStore store) { IDistributor d = store.GetDistributor(); d.ShipBook(); } Client code: Client code does not change. /* * GUIFactory example */ abstract class GUIFactory { public static GUIFactory getFactory() { int sys = readFromConfigFile("OS_TYPE"); if (sys == 0) { return new WinFactory(); } else { return new OSXFactory(); } public abstract Button createButton(); } class WinFactory extends GUIFactory { public Button createButton() { return new WinButton(); } class OSXFactory extends GUIFactory { public Button createButton() { return new OSXButton(); } abstract class Button { public abstract void paint(); } class WinButton extends Button { public void paint() { System.out.println("I'm a WinButton: "); } class OSXButton extends Button { public void paint() { System.out.println("I'm an OSXButton: "); } public class Application { public static void main(String[] args) { GUIFactory factory = GUIFactory.getFactory(); Button button = factory.createButton(); button.paint(); } // Output is either: // "I'm a WinButton:" // or: // "I'm an OSXButton:" } Suppose you are writing a program that performs remote diagnostics on computers made by a computer manufacturer called Stellar Microsystems. Over time, Stellar has produced computer models having substantially different architectures. Their oldest computers used CPU chips, MMU chips from Enginola. Since then, they have released three generations of computers based on their own ember CPU and MMU. CPU // class CPU public abstract class CPU {... } // class EmberCPU class EmberCPU extends CPU {... } // class EnginolaCPU class EnginolaCPU extends CPU {... } MMU // class MMU public abstract class MMU {... } // class EmberMMU class EmberMMU extends MMU {... } // class EnginolaMMU class EnginolaMMU extends MMU {... } ArchitectureToolKit public abstract class ArchitectureToolkit { static final ArchitectureToolkit getFactory() { // algorithm to return concrete // architecture objects } // getFactory() public abstract CPU createCPU() ; public abstract MMU createMMU() ; } // AbstractFactory class EmberToolkit extends ArchitectureToolkit { public CPU createCPU() { return new EmberCPU(); } // createCPU() public MMU createMMU() { return new EmberMMU(); } // createMMU() } // class EmberFactory class EnginolatToolkit extends ArchitectureToolkit { public CPU createCPU() { return new EnginolatCPU(); } // createCPU() public MMU createMMU() { return new EnginolatMMU(); } // createMMU() } // class EnginolatFactory public class Client { public void doIt () { AbstractFactory af; af = AbstractFactory.getFactory(); CPU cpu = af.createCPU(); } // doIt } // class Client Game development example RTS (Real Time Strategy) games like Starcraft, Warcraft, Command and Conquer, etc. One of the common things that one is required to do is build structures that will allow one to build units from the structures. A RTS game has two types of species. The Gaea Federation and the Borg. The game program allows three kinds of structures: Barracks, Headquarters and a WarRoom for the two species. Our abstract factory will create a family of units to be built from the structures for both species. AbstractBarrack GaeaBarrackBorgBarrack AbstractHeadquarter GaeaHeadquarterBorgHeadquarter AbstractWarRoom GaeaWarRoomBorgWarRoom AbstractSpecies GetFactory() CreateBarrack() CreateHeadquarter() CreateWarRoom() GaeaFederation CreateBarrack() CreateHeadquarter() CreateWarRoom() Borg CreateBarrack() CreateHeadquarter() CreateWarRoom() CreateHeadquarter(){ new GaeaHeadquarter() } CreatWarRoom(){ new GaeaWarRoom() } CreateBarrack(){ new GaeaBarrack() } AbstractSoupFactory defines the method names and return types to make various kinds of soup. The BostonConcreteSoupFactory and the HonoluluConcreteSoupFactory both extend the AbstractSoupFactory. Soup Factory Example class SoupFactory { public SoupFactory() { } public ClamChowder makeClamChowder() { return new ClamChowder(); } public FishChowder makeFishChowder() { return new FishChowder(); } class BostonSoupFactory extends SoupFactory { public ClamChowder makeClamChowder() { return new BostonClamChowder(); } public FishChowder makeFishChowder() { return new BostonFishChowder(); } } class HonoluluSoupFactory extends SoupFactory { public ClamChowder makeClamChowder() { return new HonoluluClamChowder(); } public FishChowder makeFishChowder() { return new HonoluluFishChowder(); } abstract class Soup { string soupName; } class ClamChowder extends Soup { public ClamChowder() { soupName = "ClamChowder"; } class FishChowder extends Soup { public FishChowder() { soupName = "FishChowder"; } class BostonClamChowder extends ClamChowder { public BostonClamChowder() { soupName = "QuahogChowder"; } class BostonFishChowder extends FishChowder { public BostonFishChowder() { soupName = "ScrodFishChowder"; } class BostonClamChowder extends ClamChowder { public BostonClamChowder() { soupName = "QuahogChowder"; } class BostonFishChowder extends FishChowder { public BostonFishChowder() { soupName = "ScrodFishChowder"; } class HonoluluClamChowder extends ClamChowder { public HonoluluClamChowder() { soupName = "PacificClamChowder"; } class HonoluluFishChowder extends FishChowder { public HonoluluFishChowder() { soupName = "OpakapakaFishChowder"; } Beer and Bar Example Basically there are bars and bars produce beer. You are not going to be able to order just a plain Beer from the bar. You want specific beers. Each bar is going to have its own beers. So you have your beers. Now all you need are a few bars to serve them up to you! Now, its not just good programming practice to have that abstract bar there. If you want the house beer from BarC, you have to know that BarC is serving it. Remember that after a bar or two, youd probably have no idea where you are? So enter the Abstract Bar Factory. Youll just put a method that tells you where to drink. public abstract class Bar { private static int BarCounter = 0; private static Bar[] itenerary = new Bar[4] { new BarA(), new BarB(), new BarC(), new BarD() }; public static Bar GetNextBar(){ if (BarCounter >= itenerary.Length) BarCounter = 0; return itenerary[BarCounter++]; } public Beer GetSamAdams() { return new SamAdams(); } public Beer GetMichUltra() { return new MichelobUltra(); } public abstract Beer GetHouseBeer(); } public class BarA extend Bar { public Beer GetHouseBeer() { return new BarA_stout(); } public class BarB extend Bar { public Beer GetHouseBeer() { return new BarB_liteAmber(); } public class BarC extend Bar { public Beer GetHouseBeer() { return new BarC_Crap(); } public class BarD extend Bar { public Beer GetHouseBeer() { return new BarD_JagerBomb(); } public abstract class Beer { public void Consume() { System.out.printf("Glug glug glug.... mmmmmm beer."); } public class BarA_stout extend Beer { public void Consume() { System.out.printf("Goes down smooth, nice frothy head. Goes great with a burger.....i want a burger!"); } public class BarB_liteAmber extend Beer { public void Consume() { System.out.printf("Great with wings, has a nice amber color and is an easy drink to have over conversation!"); } public class BarC_Crap extend Beer { public void Consume() { System.out.printf("Holy shit, this must be budwiser...we need to get the hell out of here, they probally spiked my Sam with water!"); } public class BarD_JagerBomb extend Beer { public void Consume() { System.out.printf("Wow....they lied, this ain't just beer..... One more round please!"); } public class YouAndYourFriend { private Random beerTolorence = new Random(); public static void main(String args[]) { int trips = beerTolorence.nextInt(4, 20); //thats a lot of beer! for (int i = 0; i < trips; i++) { WeWantBeer(); } public void WeWantBeer() { Bar currentBar = Bar.GetNextBar(); //For the you currentBar.GetMichUltra().Consume(); //for your friend currentBar.GetSamAdams().Consume(); //for you both currentBar.GetHouseBeer().Consume(); } Excellent, now, you and your friend just need to drink the beer. Note the simplicity here! You dont care where you are drinking. If its on the itinerary, you are going to drink there! And best of all, you dont need to keep track of where to go next or where weve been. Abstract Factories are great for getting drunk, just remember that and youll be OK. Summary so far.. OO Basics Abstraction Encapsulation Inheritance Polymorphism OO Principles Encapsulate what varies Favor composition over inheritance Program to interfaces not to implementations Strive for loosely coupled designs between objects that interact Classes should be open for extension but closed for modification. Depend on abstracts. Do not depend on concrete classes. Only talk to your friends Hollywood principles: dont call us, we will call you. Depend on abstracts. Do not depend on concrete classes. Summary so far OO Patterns Strategy Pattern defines a family of algorithms, Encapsulates each one, and makes them interchangeable. Strategy lets the algorithm vary independently from clients that use it. Observer Pattern defines a one-to-many dependency between objects so that when one object changes state, all of its dependents are notified and updated automatically. Decorator Pattern attach additional responsibilities to an object dynamically. Decorators provide a flexible alternative for sub-classing for extending functionality Singleton Pattern ensure a class only has one instance, and provide a global point of access to it Adapter Pattern converts the interface of a class into another interface the clients expect. Adapter lets classes work together that couldnt otherwise because of incompatible interfaces. Faade Pattern provides a unified interface to a set of interfaces in a subsystem. Faade defines a higher level interface that makes the subsystem easier to use. Template Pattern defines steps of an algorithm. Subclasses cannot change the algorithm (final). It facilitates code reuse. Factory Method Define an interface for creating an object, but let subclasses decide which class to instantiate. Factory method lets a class defer instantiation to the subclasses. Abstractor Factory Provide an interface for creating families of related or dependent objects without specifying their concrete classes.