Follow treslines by email clicking Here!

Wednesday, August 20, 2014

Programming Design Pattern - Factory Pattern Applied - Best Practise

Hi there!

Today i'm gonna show the Factory design pattern in action. The Factory design pattern is a very useful programming design pattern while dealing with creation of default, complex objects.

I'm assuming here you already know the concepts and i'll be focusing on practise. The example i will provide is a nice way to show it how it could looks like. You can always come back here, take it, adapt it and use it in your applictions as you may need. So be sure you bookmark it or join the group here on the right side of this post subscribing it.

First of all, let's take a look at the UML diagram of it. After that we will take the analogy for our example.

The UML Diagram of the Factory Pattern


Pay close attention, because once you understand that, everything will become clear and simple to understand. That's the reason I'm putting always the UML first. That way you'll get an eye for it with the time.



The example

In our example we will see, how we could implement a coffeshop like starbucks. We may think that's a complicated think, but in fact it is not. With the right design it can be very simple. Imagine you are grounding your coffee company. You want to offer lots of different coffee types. How could we do it? In this example we will see it.

The Product

Normally we will start by the products we have. That's a typical characteristic and keyword for the simple factory. All concrete coffee types will extend from it.

// DEFINE COMMON PRODUCT INTERFACE
public abstract class Coffee {
    private BigDecimal basePrice= BigDecimal.ZERO;
    private HashMap < String, BigDecimal > addOns;
    public Coffee(BigDecimal basePrice){
        addOns =  new HashMap < String, BigDecimal > ( );
        this.basePrice=basePrice;
    }
    public BigDecimal  getTotalPrice(){return this.basePrice;}
    public void addIngredient(String name, BigDecimal price){
        this.basePrice=this.basePrice.add(price);
        addOns.put(name, price);
    }
    public void removeIngredient(String name){
        final BigDecimal price = addOns.get(name);
        this.basePrice=this.basePrice.subtract(price);
        addOns.remove(name);
    }
}

The Coffee types (products)

Here we implement all coffees we intend to offer.

import java.math.BigDecimal;

public class Cappuccino extends Coffee {
    public Cappuccino(BigDecimal basePrice) {
        super(basePrice);
    }
}
public class LatteMachiatto extends Coffee {
    public LatteMachiatto(BigDecimal basePrice) {
        super(basePrice);
    }
}
public class Intenso extends Coffee {
    public Intenso(BigDecimal basePrice) {
        super(basePrice);
    }
}
public class Expresso extends Coffee {
    public Expresso(BigDecimal basePrice) {
        super(basePrice);
    }
}

The factory's common interface

Here we define the contract from the factory. Every factory implements it.

/**
 * Common interface to be used while creating simple factories.
 * @param P product to be created from this factory. The interface from the product.
 * @param A argument: an optional creation attribute. You may leave it empty.
 * @author Ricardo Ferreira
 * @version 1.0
 * @since 08/04/2014
 */
public interface Factory < P , A > {
   /**
    * creates a default product without argument or a specific product if you pass any arguments
    * @param argument an optional creation attribute
    * @return a default concrete product
    */
    P create(@SuppressWarnings("unchecked") A... arg);
}

The Factory itself

A new state of the art is to use enum's to create factories. Why? Because it is easier, cleaner and can be self-referenced as we will see.

/**
 * Simple coffee factory
 * @author Ricardo Ferreira
 * @version 1.0
 * @since 08/04/2014
 */
public enum CoffeeFactory implements Factory < Coffee, BigDecimal > {
    CAPUCCINO, LATTE_MACHIATTO, EXPRESSO, INTENSO;
    @Override
    public Coffee create(BigDecimal... arg) {
        boolean hasArg = arg.length > 0;
        switch (this) {
        case LATTE_MACHIATTO:
            return hasArg?new LatteMachiatto(arg[0]):new LatteMachiatto(new BigDecimal("2.0"));
        case CAPUCCINO:
            return hasArg?new Cappuccino(arg[0]):new Cappuccino(new BigDecimal("3.0"));
        case EXPRESSO:
            return hasArg?new Expresso(arg[0]):new Expresso(new BigDecimal("4.0"));
        case INTENSO:
            return hasArg?new Intenso(arg[0]):new Intenso(new BigDecimal("5.0"));
        default:
            return new LatteMachiatto(new BigDecimal("6.0"));
        }
    }
}

The Test

Finally, we can see how our new coffee shop factory would looks like.

public class Client {
    public static void main(String[] args) {
        // create coffe without args
        final Coffee simpleLatteMachiato = CoffeeFactory.LATTE_MACHIATTO.create();
        simpleLatteMachiato.addIngredient("choco", new BigDecimal("0.3"));
        simpleLatteMachiato.addIngredient("zugar", new BigDecimal("0.4"));
        simpleLatteMachiato.addIngredient("chantilly", new BigDecimal("0.6"));
        simpleLatteMachiato.removeIngredient("choco");
        System.out.println("Latte Machiatto: "+simpleLatteMachiato.getTotalPrice());
        // Create coffee using args
        final Coffee simpleExpresso = CoffeeFactory.EXPRESSO.create(new BigDecimal("4.0"));
        simpleExpresso.addIngredient("choco", new BigDecimal("0.3"));
        simpleExpresso.addIngredient("zugar", new BigDecimal("0.2"));
        simpleExpresso.addIngredient("chantilly", new BigDecimal("1.2"));
        System.out.println("Expresso: "+simpleExpresso.getTotalPrice());
    }
}

That's all! Hope you like it!

No comments:

Post a Comment