Download as pdf or txt
Download as pdf or txt
You are on page 1of 12

06/05/2023, 13:39 head-first-design-patterns/baking_with_oo_goodness.

md at master · stefoxp/head-first-design-patterns · GitHub

stefoxp/head-first-design-patterns Public

Code Issues Pull requests Actions Projects Security Insights

master
head-first-design-patterns / baking_with_oo_goodness.md
stefoxp c.4 Baking with OO Goodness complete
1 contributor

539 lines (425 sloc) 16.4 KB

04 Baking with OO Goodness


Factory Pattern
Pizza store sample
Start sample
Pizza orderPizza() {
Pizza pizza = new Pizza();

pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
return pizza;
}

// if you need more than one type of pizza


Pizza orderPizza(String type) {
Pizza pizza;

// we instantiate the correct concrete class and assign it to the pizz


if (type.equals("cheese")) {
pizza = new CheesePizza();
} else if (type.equals("greek")) {
pizza = new GreekPizza();

https://github.com/stefoxp/head-first-design-patterns/blob/master/baking_with_oo_goodness.md 1/12
06/05/2023, 13:39 head-first-design-patterns/baking_with_oo_goodness.md at master · stefoxp/head-first-design-patterns · GitHub

} else if (type.equals("pepperoni")) {
pizza = PepperoniPizza();
}

// each Pizza subtype knows how to prepare itself


pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
return pizza;
}

// N.B. but the pressure is on to add more pizza types

This code is NOT closed for modification.


The if ... else if code varies. As the pizza selection changes over time, you'll have to
modify this code over and over.
The pizza.prepare() ... pizza.box() code is what we expect to stay the same.
Encapsulating object creation
// First we pull this code out of the orderPizza method
if (type.equals("cheese")) {
pizza = new CheesePizza();
} else if (type.equals("greek")) {
pizza = new GreekPizza();
} else if (type.equals("pepperoni")) {
pizza = PepperoniPizza();
}

// Then we place that code in an object that is only going to worry about

Factories handle the details of object creation.


A simple pizza factory
// this class has one job in life: creating pizzas for its clients
public class SimplePizzaFactory {
// this is the method all clients will use to instantiate new objects
public Pizza createPizza(String type) {
Pizza pizza = null;

if (type.equals("cheese")) {
pizza = new CheesePizza();
} else if (type.equals("greek")) {
pizza = new GreekPizza();
} else if (type.equals("pepperoni")) {
https://github.com/stefoxp/head-first-design-patterns/blob/master/baking_with_oo_goodness.md 2/12
06/05/2023, 13:39 head-first-design-patterns/baking_with_oo_goodness.md at master · stefoxp/head-first-design-patterns · GitHub

pizza = PepperoniPizza();
}
return pizza;
}
}

public class PizzaStore {


SimplePizzaFactory factory;

// gets the factory passed to it in the constructor


public PizzaStore(SimplePizzaFactory factory) {
this.factory = factory;
}

public Pizza orderPizza(String type) {


Pizza pizza;
// uses the factory to create its pizzas by simply passing on the
pizza = factory.createPizza(type);

pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
return pizza;
}

// other methods
}

The Simple Factory isn't actually a Design Pattern it's more of a programming idiom.
Simple pizza class diagram
// the client of the factory
class PizzaStore {
orderPizza()
}

// the factory; it should be the only part of our application that refers
class SimplePizzaFactory {
createPizza()
}

// the product of the factory: pizza


abstract class Pizza {
prepare()
bake()
cut()
box()
}

https://github.com/stefoxp/head-first-design-patterns/blob/master/baking_with_oo_goodness.md 3/12
06/05/2023, 13:39 head-first-design-patterns/baking_with_oo_goodness.md at master · stefoxp/head-first-design-patterns · GitHub

// concrete products
class CheesePizza extends Pizza {}
class PepperoniPizza extends Pizza {}

A Framework for the pizza store


public abstract class PizzaStore {
public Pizza orderPizza(String type) {
Pizza pizza;

pizza = createPizza(type);

pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();

return pizza;
}

// now we've moved our factory object to this method


protected abstract Pizza createPizza(String type);

// all the responsability for instantiating Pizzas has been moved into
}

// subclasses sample for franchising

// extends PizzaStore, so it inherits the orderPizza() method


class NYStylePizzaStore extends PizzaStore {
Pizza createPizza(type) {
// for each type of Pizza we create the NY style
if (type.equals("cheese")) {
return new NYStyleCheesePizza();
} else if (type.equals("pepperoni")) {
return NYStylePepperoniPizza();
} else return null;
}
}

class ChicagoStylePizzaStore extends PizzaStore {


Pizza createPizza(type) {
if (type.equals("cheese")) {
return new ChicagoStyleCheesePizza();
} else if (type.equals("pepperoni")) {
return ChicagoStylePepperoniPizza();
} else return null;
}
}

https://github.com/stefoxp/head-first-design-patterns/blob/master/baking_with_oo_goodness.md 4/12
06/05/2023, 13:39 head-first-design-patterns/baking_with_oo_goodness.md at master · stefoxp/head-first-design-patterns · GitHub

A factory method handles object creation and encapsulates it in a subclass. This


decouples the client in the superclass from the object creation code in the subclass:
abstract Product factoryMethod(String type)

A factory method:
is abstract so the subclasses are counted on to handle object creation;
returns a Product that is typically used within methods defined in the superclass;
isolates the client (the code in the superclass) from knowing what kind of
concrete Product is actually created;
may be parametrized (or not) to select among several variations of a product;
Implementation Code Pizza Store Simple 05
The Factory Method Pattern
The Factory Method Pattern encapsulates object creation by letting subclasses
decide what objects to create.
The Creator classes - class diagram
// The Creator classes ***

abstract class PizzaStore {


// abstract creator class
// the creator never really knows wich concrete product was produced

abstract createPizza()
orderPizza()
}

class NYPizzaStore extends PizzaStore {


// concrete creator class

createPizza()
}

class ChicagoPizzaStore extends PizzaStore {


// concrete creator class

createPizza()
}
// ***

// The Product classes ***


https://github.com/stefoxp/head-first-design-patterns/blob/master/baking_with_oo_goodness.md 5/12
06/05/2023, 13:39 head-first-design-patterns/baking_with_oo_goodness.md at master · stefoxp/head-first-design-patterns · GitHub

abstract class Pizza {}

// the concrete products


class NYStyleCheesePizza extends Pizza {}
class NYStylePepperoniPizza extends Pizza {}
class NYStyleClamPizza extends Pizza {}
class NYStyleVeggiePizza extends Pizza {}

class ChicagoStyleCheesePizza extends Pizza {}


class ChicagoStylePepperoniPizza extends Pizza {}
class ChicagoStyleClamPizza extends Pizza {}
class ChicagoStyleVeggiePizza extends Pizza {}

// ***

Another way to look at this pattern as a framework is the way it encapsulates product
knowledge into each creator. The factory method is the key to encapsulating this
knowledge.
The Factory Method Pattern - class diagram
// the Creator is a class that contains the implementation for all of the
abstract class Creator {
// the abstract factoryMethod is what all Creator subclasses must impl
abstract factoryMethod()
anOperation()
}

class ConcreteCreator extends Creator {


// the ConcreteCreator implements the factoryMethod, which is the meth
factoryMethod()
}

abstract class Product {}

// the ConcreteCreator is responsible for creating one or more concrete pr


class ConcreteProduct extends Product {}

Questions:
This pattern is useful if you've only got concrete creator because you are
decoupling the implementation of the product from its use.
The factory method and the Creator class isn't always abstract. You can define a
default factory method to produce some concrete product.
Guidelines help to respect Dependency Inversion Principle
https://github.com/stefoxp/head-first-design-patterns/blob/master/baking_with_oo_goodness.md 6/12
06/05/2023, 13:39 head-first-design-patterns/baking_with_oo_goodness.md at master · stefoxp/head-first-design-patterns · GitHub

No variable should hold a reference to a concrete class. Use a factory to get


around that.
No class should derive from a concrete class. Derive from an abstraction (like an
interface or an abstract class). No method should override an implemented
method of any of its base classes.
Every single Java program ever written violates these guidelines. You'll know when
you are violating the principle and you'll have a good reason for doing so.
Ensuring consistency in your ingredients
to build a factory that produces them and ships them to your franchises. Problem:
we've got the same product families but different implementations based on region.
public interface PizzaIngredientFactory {
public Dough createDough();
public Sauce createSauce();
public Cheese createCheese();
public Veggies[] createVeggies();
public Pepperoni createPepperoni();
public Clams createClam();
}

public class NYPizzaIngredientFactory implements PizzaIngredientFactory {


// for each ingredient in the ingredient family, we create the New Yor
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(), n
return veggies;
}
// this is shared between NY and Chicago
public Pepperoni createPepperoni() {
return new SlicedPepperoni();
}
public Clams createClam() {
return new FreshClams();
}
}

public class ChicagoPizzaIngredientFactory implements PizzaIngredientFacto

// reworking the pizzas


https://github.com/stefoxp/head-first-design-patterns/blob/master/baking_with_oo_goodness.md 7/12
06/05/2023, 13:39 head-first-design-patterns/baking_with_oo_goodness.md at master · stefoxp/head-first-design-patterns · GitHub

public abstract class Pizza {


// each pizza holds a set of ingredients that are used in its preparat
String name;
Dough dough;
Sauce sauce;
Veggies veggies[];
Cheese cheese;
Pepperoni pepperoni;
Clams clams;

abstract void prepare();

// our other methods remain the same, with the exception of the prepar
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
}
}

public class CheesePizza extends Pizza {


PizzaIngredientFactory ingredientFactory;

// to make a pizza now, we need a factory to provide the ingredients


// so each Pizza class gets a factory passed into its constructor
public CheesePizza(PizzaIngredientFactory ingredientFactory) {
this.ingredientFactory = ingredientFactory;
}

void prepare() {
System.out.println("Preparing " + name);
// here's where the magic happens
// each time it needs an ingredient, it asks the factory to produc
dough = ingredientFactory.createDough();
sauce = ingredientFactory.createSauce();
cheese = ingredientFactory.createCheese();
}
}

// the client of the AbstractFactory


https://github.com/stefoxp/head-first-design-patterns/blob/master/baking_with_oo_goodness.md 8/12
06/05/2023, 13:39 head-first-design-patterns/baking_with_oo_goodness.md at master · stefoxp/head-first-design-patterns · GitHub

public class NYPizzaStore extends PizzaStore {


protected Pizza createPizza(String item) {
Pizza pizza = null;
// the NY Store is composed with a NY pizza ingredient factory.
// this will be used to produce the ingredients for all NY style p
PizzaIngredientFactory ingredientFactory = new NYPizzaIngredientFa

if(item.equals("cheese")) {
// we now pass each pizza the factory that should be used to p
pizza = new CheesePizza(ingredientFactory);
pizza.setName("New York Style Cheese Pizza");
} else if (item.equals("veggie")) {
pizza = new VeggiePizza(ingredientFactory);
pizza.setName("New York Style Veggie Pizza");
} else if (item.equals("clam")) {
pizza = new ClamPizza(ingredientFactory);
pizza.setName("New York Style Clam Pizza");
} else if (item.equals("pepperoni")) {
pizza = new PepperoniPizza(ingredientFactory);
pizza.setName("New York Style Pepperoni Pizza");
}
return pizza;
}
}

an Abstract Factory provides an interface for a family of products;


from the abstract factory, we derive one or more concrete factories that produce
the same products, but with different implementations;
we then write our code so that it uses the factory to create products. By passing
in a variety of factories, we get a variety of implementations of those products.
But our client code stays the same.
The Abstract Factory Pattern
The client is decoupled from any of the specifics of the concrete products.
The Abstract Factory Pattern - class diagram
// the AbstractFactory defines the interface that all Concrete factories m
interface AbstractFactory {
CreateProductA()
CreateProductB()
}

// the concrete factories implement the different product families. To cre


class ConcreteFactory1 implements AbstractFactory {
CreateProductA()
CreateProductB()
}

https://github.com/stefoxp/head-first-design-patterns/blob/master/baking_with_oo_goodness.md 9/12
06/05/2023, 13:39 head-first-design-patterns/baking_with_oo_goodness.md at master · stefoxp/head-first-design-patterns · GitHub

class ConcreteFactory2 implements AbstractFactory {


CreateProductA()
CreateProductB()
}

// this is the product family. Each concrete factory can produce an entire
interface AbstractProductA {}

class ProductA1 implements AbstractProductA {}


class ProductA2 implements AbstractProductA {}

interface AbstractProductB {}

class ProductB1 implements AbstractProductB {}


class ProductB2 implements AbstractProductB {}

// the Client is written against the abstract factory and then composed at
class Client {}

Often the methods of an Abstract Factory are implemented as factory methods. The
job of an Abstract Factory is to define an interface for creating a set of products. Each
method in that interface is responsible for creating a concrete product. So, factory
methods are a natural way to implement your product methods in your abstract
factories.
The Factory Method and Abstract Factory compared
The Factory Method relies on inheritance: object creation is delegated to subclasses
which implement the factory method to create objects.
The Abstract Factory relies on object composition: object creation is implemented in
methods exposed in the factory interface.
All factory patterns promote loose coupling by reducing the dependency of your
application on concrete class.
The intent of Factory Method is to allow a class to defer instantiation to its
subclasses.
The intent of Abstract Factory is to create families of related objects without having to
depend on their concrete classes.
Factory Method PizzaStore class diagram
// is implemented as a Factory Method because we want to be able to create
abstract class PizzaStore {
createPizza()
}

https://github.com/stefoxp/head-first-design-patterns/blob/master/baking_with_oo_goodness.md 10/12
06/05/2023, 13:39 head-first-design-patterns/baking_with_oo_goodness.md at master · stefoxp/head-first-design-patterns · GitHub

class NYPizzaStore extends PizzaStore {


// the Factory Method
createPizza()
}
class ChicagoPizzaStore extends PizzaStore {
// the Factory Method
createPizza()
}

// this is the product of the PizzaStore


// clients only rely on this abstract type
abstract class Pizza {}

// subclasses are instantiated by the Factory Methods


// the createPizza() method is parameterized by pizza type, so we can retu
class NYStyleCheesePizza extends Pizza {}
class NYStylePepperoniPizza extends Pizza {}

class ChicagoStyleCheesePizza extends Pizza {}


class ChicagoStylePepperoniPizza extends Pizza {}

Abstract Factory PizzaStore class diagram


// is implemented as an Abstract Factory because we need to create familie
interface PizzaIngredientFactory {
createDough();
createSauce();
createCheese();
createVeggies();
createPepperoni();
createClam();
}

// each concrete subclass creates a family of products


class NYPizzaIngredientFactory implements PizzaIngredientFactory {
// methods to create products in an Abstract Factory are often impleme
createDough();
createSauce();
createCheese();
createVeggies();
createPepperoni();
createClam();
}
class ChicagoPizzaIngredientFactory implements PizzaIngredientFactory {
createDough();
createSauce();
createCheese();
createVeggies();
createPepperoni();
createClam();

https://github.com/stefoxp/head-first-design-patterns/blob/master/baking_with_oo_goodness.md 11/12
06/05/2023, 13:39 head-first-design-patterns/baking_with_oo_goodness.md at master · stefoxp/head-first-design-patterns · GitHub

// the subclass decides the type of dough


interface Dough {}

// the product subclasses create parallel sets of product families


class ThickCrustDough implements Dough {}
class ThinCrustDough implements Dough {}

// the subclass decides the type of clams


interface Clams {}

class FrozenClams implements Clams {}


class FreshClams implements Clams {}

https://github.com/stefoxp/head-first-design-patterns/blob/master/baking_with_oo_goodness.md 12/12

You might also like