2011年12月27日 星期二

PHP Abstract Factory:物件家族的抽象工廠


在複雜的軟體系統架構中,將原來雜亂無章的組件加以分門別類,然後從中取出性質相似與功能相近的物件,使其集中群聚,就能夠大幅地簡化程式系統組件的複雜度。而 Abstract Factory 模式,正是用來協助程式設計者將物件分門別類與集中管理的好方法。
Abstract Factory 的「工廠」,與 Factory Method 的「工廠」目的相同,同樣屬於「生成模式」的分類,都是用來產生出物件成品的製造者。對於設計模式的入門者來說,很容易將 Factory Method 模式與 Abstract Factory 模式兩者的使用目的以及使用時機搞混;雖然都是屬於工廠類型的作業模式,然而 Abstract Factory 與 Factory Method 的不同之處在於,抽象工廠模式是將一組性質相關或相依的物件,放在同一個工廠裡生產。例如對於食品工廠來說,能夠生產的包含飲料、零食與泡麵三項食品類的產品;而皮件工廠,則能夠生產出皮包、皮帶與皮鞋等皮製產品。也就是在一個工廠之內有數個不同的生產線,能夠同時進行不同成品的生產作業。在實際程式系統的應用中,可以說 Abstract Factory 經常是一堆 Factory Method 的組合
以 Abstract Factory 設計模式的 UML 結構圖來看,根據《物件導向設計模式》(Design Patterns) 書中所述,其中定義了參與抽象工廠模式的幾個角色:
  • AbstractFactory:此介面宣告出可生成各抽象成品物件的操作。
  • ConcreteFactory:具體實作出可建構具象成品物件的操作。
  • AbstractProduct:宣告某成品物件類型之介面。
  • ConcreteProduct:是 ConcreteFactory 所建構的成品物件;也是 AbstractFactory 介面的具象實作。
  • Client:只觸及 AbstractFactory 和 AbstractProduct 兩抽象類別所訂之介面。
與 Factory Method 的結構相似,首先由 AbstractFactory 宣告出創建物件的抽象介面,然後再由繼承而來的子類別 ConcreteFactory 具體實作出對應於各實體成品的操作細節。AbstractProduct 與 ConcreteProduct 之間的關係亦然類似;AbstractProduct 屬於抽象基底產品,而 ConcreteProduct 是衍生而來的實體產品。對於系統外部的使用者 Client 來說,無須瞭解 ConcreteFactory 與 ConcreteProduct 的實作細節,僅需接觸 AbstractFactory 與 AbstractProduct 兩者的介面,就能夠產生出相對應的實體成品。

下圖為AbstractFactory的類別圖:


圖中AbstractFactory、Part指的是,物件必須具有
AbstractFactory、Part所定義之公開協定,而非專指Java中的interface定義。對於靜態語言來說,例如Java,必須使用型態來宣告變數,因此根據需求,可以使用interfact或abstract class來定義AbstractFactory、Part所定義之公開協定。




Sample 1:

AbstractBookFactory.php

//copyright Lawrence Truett and FluffyCat.com 2007, all rights reserved


  abstract class AbstractBookFactory {
  
    abstract function makePHPBook();
    
    abstract function makeMySQLBook();
  
  }
download source, use right-click and "Save Target As..." to save with a .php extension.


OReillyBookFactory.php

//copyright Lawrence Truett and FluffyCat.com 2007, all rights reserved


  include_once('AbstractMySQLBook.php');
  
  class OReillyMySQLBook extends AbstractMySQLBook {
  
    private $author;
    
    private $title;
    
    function __construct() {
    
      $this->author = 'George Reese, Randy Jay Yarger, and Tim King';
      $this->title  = 'Managing and Using MySQL';
 
    }
  
    function getAuthor() {return $this->author;}
    
    function getTitle() {return $this->title;}
  
  }
download source, use right-click and "Save Target As..." to save with a .php extension.


SamsBookFactory.php

//copyright Lawrence Truett and FluffyCat.com 2005, all rights reserved


  include_once('AbstractBookFactory.php');
  
  include_once('SamsPHPBook.php');
  include_once('SamsMySQLBook.php');
  
  class SamsBookFactory extends AbstractBookFactory {
  
    private $context = "Sams";   
  
    function makePHPBook() {return new SamsPHPBook;}
    
    function makeMySQLBook() {return new SamsMySQLBook;}
  
  }
download source, use right-click and "Save Target As..." to save with a .php extension.


AbstractBook.php

//copyright Lawrence Truett and FluffyCat.com 2007, all rights reserved
  
    abstract class AbstractBook {
  
    abstract function getAuthor();
    
    abstract function getTitle();
  
  }

Sample 2:

AbstractMySQLBook.php

//copyright Lawrence Truett and FluffyCat.com 2007, all rights reserved
  
    include_once('AbstractBook.php');
  
    abstract class AbstractMySQLBook {
  
    private $subject = "MySQL";
  
  }
download source, use right-click and "Save Target As..." to save with a .php extension.


OReillyMySQLBook.php

//copyright Lawrence Truett and FluffyCat.com 2007, all rights reserved


  include_once('AbstractMySQLBook.php');
  
  class OReillyMySQLBook extends AbstractMySQLBook {
  
    private $author;
    
    private $title;
    
    function __construct() {
    
      $this->author = 'George Reese, Randy Jay Yarger, and Tim King';
      $this->title  = 'Managing and Using MySQL';
 
    }
  
    function getAuthor() {return $this->author;}
    
    function getTitle() {return $this->title;}
  
  }
download source, use right-click and "Save Target As..." to save with a .php extension.


SamsMySQLBook.php

//copyright Lawrence Truett and FluffyCat.com 2007, all rights reserved


  include_once('AbstractMySQLBook.php');
  
  class SamsMySQLBook extends AbstractMySQLBook {
  
    private $author;
    
    private $title;
    
    function __construct() {
    
      $this->author = 'Paul Dubois';
      $this->title  = 'MySQL, 3rd Edition';
 
    }
  
    function getAuthor() {return $this->author;}
    
    function getTitle() {return $this->title;}
  
  }
download source, use right-click and "Save Target As..." to save with a .php extension.


AbstractPHPBook.php

//copyright Lawrence Truett and FluffyCat.com 2007, all rights reserved
  
    include_once('AbstractBook.php');
  
    abstract class AbstractPHPBook {
  
    private $subject = "PHP";
  
  }
download source, use right-click and "Save Target As..." to save with a .php extension.


OReillyPHPBook.php

//copyright Lawrence Truett and FluffyCat.com 2007, all rights reserved


  include_once('AbstractPHPBook.php');
  
  class OReillyPHPBook extends AbstractPHPBook {
  
    private $author;
    
    private $title;
    
    private static $oddOrEven = 'odd';
    
    function __construct() {
    
    
    
      //alternate between 2 books
      
      if ('odd' == self::$oddOrEven) {
        $this->author = 'Rasmus Lerdorf and Kevin Tatroe';
        $this->title  = 'Programming PHP';
        self::$oddOrEven = 'even';
      } else {
        $this->author = 'David Sklar and Adam Trachtenberg';
        $this->title  = 'PHP Cookbook'; 
        self::$oddOrEven = 'odd';
      }  
    }
  
    function getAuthor() {return $this->author;}
    
    function getTitle() {return $this->title;}
  
  }
download source, use right-click and "Save Target As..." to save with a .php extension.


SamsPHPBook.php

//copyright Lawrence Truett and FluffyCat.com 2007, all rights reserved


  include_once('AbstractPHPBook.php');
  
  class SamsPHPBook extends AbstractPHPBook {
  
    private $author;
    
    private $title;
    
    function __construct() {
    
      //alternate randomly between 2 books
      
      mt_srand((double)microtime()*10000000);
      $rand_num = mt_rand(0,1);      
      
      if (1 > $rand_num) {
        $this->author = 'George Schlossnagle';
        $this->title  = 'Advanced PHP Programming';
      } else {
        $this->author = 'Christian Wenz';
        $this->title  = 'PHP Phrasebook'; 
      }  
    }
  
    function getAuthor() {return $this->author;}
    
    function getTitle() {return $this->title;}
  
  }
download source, use right-click and "Save Target As..." to save with a .php extension.


testAbstractFactory.php

//copyright Lawrence Truett and FluffyCat.com 2005, all rights reserved


  include_once('OReillyBookFactory.php');  
  include_once('SamsBookFactory.php');


  echo tagins("html");
  echo tagins("head");  
  echo tagins("/head");  
  echo tagins("body");


  echo "BEGIN TESTING ABSTRACT FACTORY PATTERN";
  echo tagins("br").tagins("br");
  
  echo 'testing OReillyBookFactory'.tagins("br");
  $bookFactoryInstance = new OReillyBookFactory;
  testConcreteFactory($bookFactoryInstance);
  
  echo tagins("br");
  
  echo 'testing SamsBookFactory'.tagins("br");
  $bookFactoryInstance = new SamsBookFactory;
  testConcreteFactory($bookFactoryInstance);  
  
  echo tagins("br");
  echo "END TESTING ABSTRACT FACTORY PATTERN";
  echo tagins("br");
  
  echo tagins("/body");
  echo tagins("/html");


  function testConcreteFactory($bookFactoryInstance) {
    $phpBookOne = $bookFactoryInstance->makePHPBook();
    echo 'first php Author: '. 
   $phpBookOne->getAuthor().tagins("br");
    echo 'first php Title: '.
   $phpBookOne->getTitle().tagins("br");
   
    $phpBookTwo = $bookFactoryInstance->makePHPBook();
    echo 'second php Author: '.
   $phpBookTwo->getAuthor().tagins("br");
    echo 'second php Title: '.
   $phpBookTwo->getTitle().tagins("br");
    
 $mySqlBook = $bookFactoryInstance->makeMySQLBook();
    echo 'MySQL Author: '.
   $mySqlBook->getAuthor().tagins("br");
    echo 'MySQL Title: '.
   $mySqlBook->getTitle().tagins("br");
  }
  
  //doing this so code can be displayed without breaks
  function tagins($stuffing) {
    return "<".$stuffing.">";
  }
download source, use right-click and "Save Target As..." to save with a .php extension.

output of testAbstractFactory.php

BEGIN TESTING ABSTRACT FACTORY PATTERN


testing OReillyBookFactory
first php Author: Rasmus Lerdorf and Kevin Tatroe
first php Title: Programming PHP
second php Author: David Sklar and Adam Trachtenberg
second php Title: PHP Cookbook
MySQL Author: George Reese, Randy Jay Yarger, and Tim King
MySQL Title: Managing and Using MySQL


testing SamsBookFactory
first php Author: Christian Wenz
first php Title: PHP Phrasebook
second php Author: George Schlossnagle
second php Title: Advanced PHP Programming
MySQL Author: Paul Dubois
MySQL Title: MySQL, 3rd Edition


END TESTING ABSTRACT FACTORY PATTERN

沒有留言: