Factory Method and Abstract Factory Pattern Pats Pizza
Factory Method and Abstract Factory Pattern
Pat’s Pizza creation Pizza order. Pizza(String type){ Pizza pizza; if (type. equals(“cheese”)) { pizza = new Cheese. Pizza(); } else if type. equals(“greek”)) { pizza = new Greek. Pizza(); } else if type. equals(“pepperoni”)) { pizza = new Pepperoni. Pizza(); } Pizza. Store pizza. prepare(); pizza. bake(); pizza. cut(); pizza. box() return pizza; +order. Pizza(string): } Pizza
Identifying the aspects that vary �If the pizza shop decides to change the types of pizza it offers, the order. Pizza method has to be changed.
Pizza order. Pizza(String type){ Pizza pizza; if (type. equals(“cheese”)) { pizza = new Cheese. Pizza(); } else if type. equals(“greek”)) { pizza = new Greek. Pizza(); } else if type. equals(“pepperoni”)) { pizza = new Pepperoni. Pizza(); } pizza. prepare(); Part that pizza. bake(); remains pizza. cut(); constant pizza. box() } Part that varies. Pizza prepare() bake() cut() box()
Encapsulating object creation if (type. equals(“cheese”)) { pizza = new Cheese. Pizza(); } else if type. equals(“greek”)) { pizza = new Greek. Pizza(); } else if type. equals(“pepperoni”)) { pizza = new Pepperoni. Pizza(); } Simple. Pizza. Factory
Building a simple pizza factory public class Simple. Pizza. Factory { public Pizza create. Pizza(String type) { Pizza pizza = null; if (type. equals("cheese")) { pizza = new Cheese. Pizza(); } else if (type. equals("pepperoni")) { pizza = new Pepperoni. Pizza(); } else if (type. equals("clam")) { pizza = new Clam. Pizza(); } else if (type. equals("veggie")) { pizza = new Veggie. Pizza(); } return pizza; } } Simple. Pizza. Factory +create. Pizza(string): Pizza
Reworking the Pizza. Store Class public class Pizza. Store { Simple. Pizza. Factory factory; public Pizza. Store(Simple. Pizza. Factory factory) { this. factory = factory; } public Pizza order. Pizza(String type) { Pizza pizza; pizza = factory. create. Pizza(type); Pizza. Store pizza. prepare(); pizza. bake(); - factory: pizza. cut(); Simple. Pizza. Factory pizza. box(); return pizza; } +order. Pizza(string): } Pizza
Example for Simple Factory Simple. Pizza. Factory factory = new Simple. Pizza. Factory(); Pizza. Store store = new Pizza. Store(factory); Pizza pizza = store. order. Pizza("cheese");
Simple Factory Defined
Pizza. Store Simple. Pizza. Factory - factory: Simple. Pizza. Factory +order. Pizza(string): Pizza +create. Pizza(string): Pizza prepare() bake() cut() box() Cheese. Pizza Clam. Pizza Veggie. Pizza
Creating multiple factories NYPizza. Factory Pizza. Store Chicago. Pizza. Factory
Allowing the subclasses to decide 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.
Simple. Pizza. Factory +create. Pizza() NYPizza. Factory +create. Pizza() Chicago. Pizza. Factory +create. Pizza()
class NYSimple. Factory extends Simple. Factory{ Pizza create. Pizza(String type){//creates Newyork specific Pizza pizza; . . . return pizza; } } class Pizza. Store { Simple. Factory factory; public Pizza. Store(Simple. Factory fact){ factory = fact; } Pizza order. Pizza(String type){ Pizza p = factory. create. Pizza(type); p. prepare(); p. bake(); p. cut(); p. box(); } }
Creating multiple instances NYPizza. Factory ny. Factory = new NYPizza. Factory(); Pizza. Store ny. Store = new Pizza. Store(ny. Factory); Pizza pizza = ny. Store. order. Pizza("cheese"); Chicago. Pizza. Factory chicago. Factory = new Chicago. Pizza. Factory(); Pizza. Store chicago. Store = new Pizza. Store(chicago. Factory); Pizza pizza = chicago. Store. order. Pizza("cheese");
Simple Factory Pizza. Store Factory: Simply. Pizza. Factory order. Pizza() Simple. Pizza. Factory create. Pizza() NYStyle. Pizza. Factory create. Pizza() Chicago. Style. Pizza. Factory create. Pizza() Pizza prepare() bake() cut() box() Cheese. Pizza Veggie. Pizza
Pizza. Store Simple. Pizza. Factory - factory: Simple. Pizza. Factory +create. Pizza(string): Pizza +order. Pizza(string): Pizza prepare() bake() cut() box() NYCheese. Pizza NYClam. Pizza Chicago. Cheese. Pizza Chicago. Pizza. Factory +create. Pizza() Chicago. Clam. Pizza
Allowing the subclasses to decide Pizza. Store create. Pizza() order. Pizza() NYStyle. Pizza. Store create. Pizza() Chicago. Style. Pizza. Store create. Pizza() 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 Pizza. Store { abstract Pizza create. Pizza(String item); public Pizza order. Pizza(String type) { Pizza pizza = create. Pizza(type); pizza. prepare(); pizza. bake(); pizza. cut(); pizza. box(); return pizza; } }
class NYPizza. Store extends Pizza. Store{ Pizza create. Pizza(String type){ . . . return pizza; } }
Creating multiple instances Pizza. Store ny. Store = new NYPizza. Store(); Pizza pizza = ny. Store. order. Pizza("cheese"); Pizza. Store chicago. Store = new Chicago. Pizza. Store(); Pizza pizza = chicago. Store. order. Pizza("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
Pizza. Store Factory Method Pattern Pizza. Store create. Pizza() order. Pizza() Pizza Prepare() Bake() Cut() Box() Creator Classes NYStyle. Pizza. Store create. Pizza() Chicago. Style. Pizza. Store create. Pizza() Product Classes NYStyle. Cheese. Pizza Ch. Style. Cheese. Pizza
Pizza. Store order. Pizza(){ Pizz p=create. Pizza(); P. prepare(); p. bake(); p. cut(); p. box(); } Pizza prepare() bake() cut() box() NYCheese. Pizza NYClam. Pizza +create. Pizza(string): Pizza +order. Pizza(string): Pizza NYPizza. Store Chicago. Pizza. Store +create. Pizza() Chicago. Cheese. Pizza Chicago. Clam. Pizza
Looking at object dependencies Pizza Store Ny. Style Cheeze Ny. Style Pizza Cheeze Pizza Clam Pizza Chicago Cheeze Chicago Pizza Cheeze Pizza 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 Pizza is an abstract class Pizza Ny. Style Cheeze Ny. Style Pizza Cheeze Pizza Clam Pizza Chicago Cheeze Chicago Pizza Cheeze Pizza Clam Pizza
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: Frozen. Clams, Plum. Tomato. Sauce, Thick. Crust. Dough, Mozzarella. Cheese � New York: Fresh. Clams, Marinaro. Sauce, Thin. Crust. Dough, Reggiano. Cheese � California: Calamari, Bruuuschetta. Sauce, Very. Thin. Crust, Goat. Cheese
Building the ingredient factories public interface Pizza. Ingredient. Factory { public Dough create. Dough(); public Sauce create. Sauce(); public Cheese create. Cheese(); public Veggies[] create. Veggies(); public Pepperoni create. Pepperoni(); public Clams create. Clam(); }
Building NY ingredient factory public class NYPizza. Ingredient. Factory implements Pizza. Ingredient. Factory { public Dough create. Dough() { return new Thin. Crust. Dough(); } public Sauce create. Sauce() { return new Marinara. Sauce(); } public Cheese create. Cheese() { return new Reggiano. Cheese(); } public Veggies[] create. Veggies() { Veggies veggies[] = { new Garlic(), new Onion(), new Mushroom(), new Red. Pepper() }; return veggies; } public Pepperoni create. Pepperoni() { return new Sliced. Pepperoni(); } public Clams create. Clam() { return new Fresh. Clams(); } }
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 } void cut() { System. out. println("Cutting the } void box() { System. out. println("Place pizza } void set. Name(String name) { this. name = name; } String get. Name() { return name; } public String to. String() { \ code to } } minutes at 350"); pizza into diagonal slices"); in official Pizza. Store box"); 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 Abstract Product Concrete Product Concrete Factory
Abstract Factory Pattern <<Interface>> Abstract. Factory Create. Product. A() Create. Product. B() Concrete. Factory 1 Concrete. Factory 2 Create. Product. A() Create. Product. B() Client <<Interface>> Abstract. Prodcut. A Product. A 2 Product. A 1 <<Interface>> Abstract. Prodcut. B Product. B 2 Product. B 1
Real world scenarios �Windows XP demonstrates the Abstract Factory pattern in its ability to instantiate families of Windows Forms elements.
On-line book order Let’s 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 don’t stock the books. You have many distributors that you can choose to send the books directly to your customers.
• If the customer is in the east coast, you will use East. Coast. Distributor • If the customer is in the mid-west, you will use Mid. West. Distributor • If the customer is in the west coast, you will use West. Coast. Distributor
Client code: //the client gets the distributor without having //to know which distributor is being used IDistributor b = book. Store. Get. Distributor();
Taking another step further, we can abstract out the Book. Store as an interface and have more types of bookstores, as shown below:
IBook. Store interface //the factory public interface IBook. Store { IDistributor Get. Distributor(); } //concrete factory public class Book. Store. A : IBook. Store { private Customer. Location location; public Book. Store. A(Customer. Location location) { this. location = location; } IDistributor IBook. Store. Get. Distributor() { //internal logic on which distributor to return //*** logic can be changed without changing the client code **** switch (location) { case Customer. Location. East. Coast: return new East. Coast. Distributor(); case Customer. Location. Mid. West: return new Mid. West. Distributor(); case Customer. Location. West. Coast: return new West. Coast. Distributor(); } return null; } }
IBook. Store interface //the product public interface IDistributor { void Ship. Book(); } //concrete product public class East. Coast. Distributor : IDistributor { void IDistributor. Ship. Book() { Console. Write. Line("Book shipped by East Coast Distributor"); } } //concrete product public class Mid. West. Distributor : IDistributor { void IDistributor. Ship. Book() { Console. Write. Line("Book shipped by Mid West Distributor"); } } //conceret product public class West. Coast. Distributor : IDistributor { void IDistributor. Ship. Book() { Console. Write. Line("Book shipped by West Coast Distributor"); } }
Client code: public enum Customer. Location { East. Coast, Mid. West, West. Coast } class Program { static void Main(string[] args) { Console. Write. Line("East Coast Customer: "); IBook. Store bookstore = new Book. Store. A(Customer. Location. East. Coast); Ship. Book(bookstore); Console. Write. Line("Mid West Customer: "); bookstore = new Book. Store. A(Customer. Location. Mid. West); Ship. Book(bookstore); Console. Write. Line("West Coast Customer: "); bookstore = new Book. Store. A(Customer. Location. West. Coast); Ship. Book(bookstore); } } private static void Ship. Book(IBook. Store store) { IDistributor d = store. Get. Distributor(); d. Ship. Book(); } Client code does not change.
/* * GUIFactory example */ abstract class GUIFactory { public static GUIFactory get. Factory() { int sys = read. From. Config. File("OS_TYPE"); if (sys == 0) { return new Win. Factory(); } else { return new OSXFactory(); } } public abstract Button create. Button(); } class Win. Factory extends GUIFactory { } public Button create. Button() { return new Win. Button(); }
class OSXFactory extends GUIFactory { public Button create. Button() { return new OSXButton(); } } abstract class Button { public abstract void paint(); } class Win. Button extends Button { public void paint() { System. out. println("I'm a Win. Button: "); } } 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. get. Factory(); Button button = factory. create. Button(); button. paint(); // Output is either: // "I'm a Win. Button: " // 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 Ember. CPU extends CPU { . . . } // class Enginola. CPU extends CPU { . . . }
MMU // class MMU public abstract class MMU { . . . } // class Ember. MMU extends MMU { . . . } // class Enginola. MMU extends MMU { . . . }
Architecture. Tool. Kit public abstract class Architecture. Toolkit { static final Architecture. Toolkit get. Factory() { // algorithm to return concrete // architecture objects } // get. Factory() public abstract CPU create. CPU() ; public abstract MMU create. MMU() ; } // Abstract. Factory
class Ember. Toolkit extends Architecture. Toolkit { public CPU create. CPU() { return new Ember. CPU(); } // create. CPU() public MMU create. MMU() { return new Ember. MMU(); } // create. MMU() } // class Ember. Factory
class Enginolat. Toolkit extends Architecture. Toolkit { public CPU create. CPU() { return new Enginolat. CPU(); } // create. CPU() public MMU create. MMU() { return new Enginolat. MMU(); } // create. MMU() } // class Enginolat. Factory
public class Client { public void do. It () { Abstract. Factory af; af = Abstract. Factory. get. Factory(); CPU cpu = af. create. CPU(); } // do. It } // class Client
Game development example �RTS (Real Time Strategy) games like Starcraft, Warcraft, Command 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 War. Room for the two species. �Our abstract factory will create a family of units to be built from the structures for both species.
Abstract. Species Get. Factory() Create. Barrack() Create. Headquarter() Create. War. Room() Abstract. Barrack Gaea. Federation Borg Create. Barrack() Create. Headquarter() Create. War. Room() Abstract. War. Room Gaea. War. Room Create. Headquarter(){ new Gaea. Headquarter() } Create. Barrack(){ new Gaea. Barrack() } Creat. War. Room(){ new Gaea. War. Room() } Borg. Barrack Borg. War. Room Abstract. Headquarter Gaea. Headquarter Borg. Headquarter
Soup Factory Example �Abstract. Soup. Factory defines the method names and return types to make various kinds of soup. The Boston. Concrete. Soup. Factory and the Honolulu. Concrete. Soup. Factory both extend the Abstract. Soup. Factory.
class Soup. Factory { public Soup. Factory() { } public Clam. Chowder make. Clam. Chowder() { return new Clam. Chowder(); } public Fish. Chowder make. Fish. Chowder() { return new Fish. Chowder(); } }
class Boston. Soup. Factory extends Soup. Factory { public Clam. Chowder make. Clam. Chowder() { return new Boston. Clam. Chowder(); } public Fish. Chowder make. Fish. Chowder() { return new Boston. Fish. Chowder(); } } class Honolulu. Soup. Factory extends Soup. Factory { public Clam. Chowder make. Clam. Chowder() { return new Honolulu. Clam. Chowder(); } } public Fish. Chowder make. Fish. Chowder() { return new Honolulu. Fish. Chowder(); }
abstract class Soup { string soup. Name; } class Clam. Chowder extends Soup { public Clam. Chowder() { soup. Name = "Clam. Chowder"; … } } class Fish. Chowder extends Soup { public Fish. Chowder() { soup. Name = "Fish. Chowder"; … } }
class Boston. Clam. Chowder extends Clam. Chowder { public Boston. Clam. Chowder() { soup. Name = "Quahog. Chowder"; … } } class Boston. Fish. Chowder extends Fish. Chowder { public Boston. Fish. Chowder() { soup. Name = "Scrod. Fish. Chowder"; … } }
class Honolulu. Clam. Chowder extends Clam. Chowder { public Honolulu. Clam. Chowder() { soup. Name = "Pacific. Clam. Chowder"; … } } class Honolulu. Fish. Chowder extends Fish. Chowder { public Honolulu. Fish. Chowder() { soup. Name = "Opaka. Fish. Chowder"; … } }
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, it’s not just good programming practice to have that abstract bar there. �If you want the house beer from Bar. C, you have to know that Bar. C is serving it. �Remember that after a bar or two, you’d probably have no idea where you are? �So enter the Abstract Bar Factory. �You’ll just put a method that tells you where to drink.
public abstract class Bar { private static int Bar. Counter = 0; private static Bar[] itenerary = new Bar[4] { new Bar. A(), new Bar. B(), new Bar. C(), new Bar. D() }; public static Bar Get. Next. Bar(){ if (Bar. Counter >= itenerary. Length) Bar. Counter = 0; return itenerary[Bar. Counter++]; } public Beer Get. Sam. Adams() { return new Sam. Adams(); } public Beer Get. Mich. Ultra() { return new Michelob. Ultra(); } public abstract Beer Get. House. Beer(); }
public class Bar. A extend Bar { public Beer Get. House. Beer() { return new Bar. A_stout(); } } public class Bar. B extend Bar { public Beer Get. House. Beer() { return new Bar. B_lite. Amber(); } } public class Bar. C extend Bar { public Beer Get. House. Beer() { return new Bar. C_Crap(); } } public class Bar. D extend Bar { public Beer Get. House. Beer() { return new Bar. D_Jager. Bomb(); } }
public abstract class Beer { public void Consume() { System. out. printf("Glug glug. . mmmmmm beer. "); } } public class Bar. A_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 Bar. B_lite. Amber 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 Bar. C_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 Bar. D_Jager. Bomb extend Beer { public void Consume() { System. out. printf("Wow. . they lied, this ain't just beer. . . One more round please!"); } }
public class You. And. Your. Friend { private Random beer. Tolorence = new Random(); public static void main(String args[]) { int trips = beer. Tolorence. next. Int(4, 20); //thats a lot of beer! for (int i = 0; i < trips; i++) { We. Want. Beer(); } } public void We. Want. Beer() { Bar current. Bar = Bar. Get. Next. Bar(); //For the you current. Bar. Get. Mich. Ultra(). Consume(); //for your friend current. Bar. Get. Sam. Adams(). Consume(); //for you both current. Bar. Get. House. Beer(). Consume(); } }
�Excellent, now, you and your friend just need to drink the beer. �Note the simplicity here! You don’t care where you are drinking. �If it’s on the itinerary, you are going to drink there! �And best of all, you don’t need to keep track of where to go next or where we’ve been. �Abstract Factories are great for getting drunk, just remember that and you’ll 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: don’t 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 couldn’t otherwise because of incompatible interfaces. � Façade Pattern provides a unified interface to a set of interfaces in a subsystem. Façade 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.
- Slides: 79