Monday, February 08, 2010

Design patterns : Factory Method

Factory method defines an interface for creating an object, but lets the subclasses decide which class to instantiate. Factory method lets a class defer instantiation to subclasses.

Factory method makes the design more customizable and only a little more complicated. Other design patterns require new classes whereas factory method requires only a new operation.

Factory method is quite similar to the Abstract Factory. In fact every relevant method of Abstract Factory is a Factory Method. Factory methods could be moved out of their classes in subsequent refactoring and evolve in an external Abstract Factory.

The participating actors are the "Product" - the abstraction of the created object, a "concrete product" - an implementation of the product. "Creator" is the base class which declares a default Factory Method and "ConcreteCreator" is a subclass of the creator which overrides the Factory Method to return a Concrete Product.

Lets check some code

PHP : lets check some code similar to those we wrote for Abstract Factory
abstract class AbstractFactoryMethod
{
  abstract function makeBook($param);
}

class OReillyFactoryMethod extends AbstractFactoryMethod
{
  private $context = "OReilly";
  function makeBook($param)
  {
    $book = NULL;
    switch($param)
    {
      case "PHP":
        $book = new OReillyPHPBook;
      break;
      case "MySQL":
        $book = new OReillyMySQLBook;
      break;
      default:
      $book = new OReillyPHPBook;
      break;
    }
    return $book;
  }
}

class SAMSFactoryMethod extends AbstractFactoryMethod
{
  private $context = "SAMS";
  function makeBook($param)
  {
    $book = NULL;
    switch($param)
    {
      case "PHP":
        $book = new SamsPHPBook;
      break;
      case "MySQL":
        $book = new SamsMySQLBook;
      break;
      default:
      $book = new SamsMySQLBook;
      break;
    }
    return $book;
  }
}

abstract class AbstractBook
{
  abstract function getAuthor();
  abstract function getTitle();
}

class OReillyPHPBook extends AbstractBook
{
  private $author;
  private $title;
  function __construct()
  {
    $this->author = "Author - OReilly php";
    $this->title = "Title - OReilly php";
  }

  function getAuthor() {return $this->author;}
  function getTitle() {return $this->title;}
}

class SamsPHPBook extends AbstractBook
{
  private $author;
  private $title;
  function __construct()
  {
    $this->author = "Author - SAMS php";
    $this->title = "Title - SAMS php";
  }

  function getAuthor() {return $this->author;}
  function getTitle() {return $this->title;}
}

class OReillyMySQLBook extends AbstractBook
{
  private $author;
  private $title;
  function __construct()
  {
    $this->author = "Author - OReilly MySQL";
    $this->title = "Title - OReilly MySQL";
  }

  function getAuthor() {return $this->author;}
  function getTitle() {return $this->title;}
}

class SamsMySQLBook extends AbstractBook
{
  private $author;
  private $title;
  function __construct()
  {
    $this->author = "Author - SAMS MySQL";
    $this->title = "Title - SAMS MySQL";
  }

  function getAuthor() {return $this->author;}
  function getTitle() {return $this->title;}
}

function testFactoryMethod($factoryMethodInstance)
{
  $phpBook = $factoryMethodInstance->makeBook("PHP");
  echo "php author : ".$phpBook->getAuthor()."\n";
  echo "php title : ".$phpBook->getTitle()."\n";

  $mysqlBook = $factoryMethodInstance->makeBook("MySQL");
  echo "mysql author : ".$mysqlBook->getAuthor()."\n";
  echo "mysql title : ".$mysqlBook->getTitle()."\n";
}

echo("Begin\n");
echo("Testing OReillyFactoryMethod\n");
$bookMethodInstance = new OReillyFactoryMethod;
testFactoryMethod($bookMethodInstance);
echo("----\n");

echo("Testing SamsFactoryMethod\n");
$bookMethodInstance = new SamsFactoryMethod;
testFactoryMethod($bookMethodInstance);
echo("----\n");

Output : 

Begin
Testing OReillyFactoryMethod
php author : Author - OReilly php
php title : Title - OReilly php
mysql author : Author - OReilly MySQL
mysql title : Title - OReilly MySQL
----
Testing SamsFactoryMethod
php author : Author - SAMS php
php title : Title - SAMS php
mysql author : Author - SAMS MySQL
mysql title : Title - SAMS MySQL
----

And some example in java

abstract class Pizza 
{
  public abstract int getPrice(); // count the cents
}

class HamAndMushroomPizza extends Pizza 
{
  public int getPrice() 
  {
    return 850;
  }
}

class DeluxePizza extends Pizza 
{
  public int getPrice() 
  {
    return 1050;
  }
}

class HawaiianPizza extends Pizza 
{
  public int getPrice() 
  {
    return 1150;
  }
}

class PizzaFactory 
{
  public enum PizzaType 
  {
    HamMushroom,
      Deluxe,
      Hawaiian
  }

  public static Pizza createPizza(PizzaType pizzaType) 
  {
    switch (pizzaType) 
    {
      case HamMushroom:
        return new HamAndMushroomPizza();
      case Deluxe:
        return new DeluxePizza();
      case Hawaiian:
        return new HawaiianPizza();
    }
    throw new IllegalArgumentException("The pizza type " + pizzaType + " is not recognized.");
  }
}

class myPizza
{
  // Create all available pizzas and print their prices
  public static void main (String args[]) 
  {
    for (PizzaFactory.PizzaType pizzaType : PizzaFactory.PizzaType.values()) 
    {
      System.out.println("Price of " + pizzaType + " is " + PizzaFactory.createPizza(pizzaType).getPrice());
    }
  }
}

Output : 

$ java myPizza 
 Price of HamMushroom is 850
 Price of Deluxe is 1050
 Price of Hawaiian is 1150


No comments: