Factory Method and Abstract Factory Pattern

advertisement
Pat’s 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();
Part that
pizza.bake();
remains
pizza.cut();
constant
pizza.box()
}
Part
that
varies.
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()
CheesePizza
Pizza
prepare()
bake()
cut()
box()
ClamPizza
VeggiePizza
ClamPizza
Creating multiple factories
NYPizzaFactory
PizzaStore
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
PizzaStore
Factory:
SimplyPizzaFactory
orderPizza()
SimplePizzaFactory
createPizza()
NYStylePizzaFactory
createPizza()
ChicagoStylePizzaFactory
createPizza()
Pizza
prepare()
bake()
cut()
box()
CheesePizza
VeggiePizza
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()
orderPizza(){
Pizz p=createPizza();
P.prepare();
p.bake();
p.cut();
p.box();
}
Pizza
Prepare()
Bake()
Cut()
Box()
Creator Classes
NYStylePizzaStore
createPizza()
ChicagoStylePizzaStore
createPizza()
Product Classes
NYStyleCheesePizza
NYStyleCheesePizza
NYStyleCheesePizza
NYStyleCheesePizza
ChStyleCheesePizza
ChStyleCheesePizza
ChStyleCheesePizza
ChStyleCheesePizza
Looking at object dependencies
Pizza
Store
NyStyle
NyStyle
Cheeze
NyStyle
Cheeze
NyStyle
Pizza
Cheeze
Pizza
Clam
Pizza
Pizza
Chicago
Chicago
Cheeze
Chicago
Cheeze
Chicago
Pizza
Cheeze
Pizza
Clam
Pizza
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
NyStyle
NyStyle
Cheeze
NyStyle
Cheeze
NyStyle
Pizza
Cheeze
Pizza
Clam
Pizza
Pizza
Chicago
Chicago
Cheeze
Chicago
Cheeze
Chicago
Pizza
Cheeze
Pizza
Clam
Pizza
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: 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
}
void cut() {
System.out.println("Cutting the
}
void box() {
System.out.println("Place pizza
}
void setName(String name) {
this.name = name;
}
String getName() {
return name;
}
public String toString() { \\ code to
}
}
minutes at 350");
pizza into diagonal slices");
in official PizzaStore 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
Product
Concrete
Factory
Abstract
Product
Concrete
Product
Concrete
Product
Concrete
Factory
Abstract Factory Pattern
<<Interface>>
AbstractFactory
CreateProductA()
CreateProductB()
ConcreteFactory1
ConcreteFactory2
CreateProductA()
CreateProductB()
CreateProductA()
CreateProductB()
Client
<<Interface>>
AbstractProdcutA
ProductA2
ProductA1
<<Interface>>
AbstractProdcutB
ProductB2
ProductB1
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 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");
}
}
Client code:
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
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.
AbstractSpecies
GetFactory()
CreateBarrack()
CreateHeadquarter()
CreateWarRoom()
AbstractBarrack
GaeaBarrack
GaeaFederation
Borg
CreateBarrack()
CreateHeadquarter()
CreateWarRoom()
CreateBarrack()
CreateHeadquarter()
CreateWarRoom()
AbstractWarRoom
GaeaWarRoom
CreateHeadquarter(){
new GaeaHeadquarter()
}
CreateBarrack(){
new GaeaBarrack()
}
CreatWarRoom(){
new GaeaWarRoom()
}
BorgBarrack
BorgWarRoom
AbstractHeadquarter
GaeaHeadquarter
BorgHeadquarter
Soup Factory Example
 AbstractSoupFactory defines the method names and
return types to make various kinds of soup.
The BostonConcreteSoupFactory and the
HonoluluConcreteSoupFactory both extend the
AbstractSoupFactory.
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, it’s 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, 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 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 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.
Download