【PHP设计模式】创建型之工厂模式(Factory Method)

工厂方法(Factory Method)

意图:

  【GoF】定义一个用于创建对象的接口,让子类决定实例化哪一个类。Factory Method使得一个类的实例化延迟到其子类。

动机:

  考虑一个个人事务管理的项目,他可以管理预约对象(appointment)。使用bolg进行信息交流,以后还可以使用不同的方式进行交流。预约的内容是ApptEncoder类,使用内容信息与第三方通信的类是CommsManager类。CommsManager即创建者,而ApptEncoder即产品。这里有很多的ApptEncoder类负责实现具体的ApptEncoder,如何得到具体的ApptEncoder对象呢?

适用:

  定义了一个创建对象的接口,即工厂接口,工厂接口的子类即工厂类用于创建对象,一个子类创建一个具体的产品类。

  一、当一个类不知道它所必须创建的对象的类的时候。

  二、当一个类希望由它的子类来指定它所创建的对象的时候。

  三、当类将创建对象这个职责委托个多个帮助子类的其中一个,并且希望将哪一个帮助子类帮助子类是代理者局部化的时候。

类图:

 

IProduct:产品接口。Creator:工厂接口。ConcrteProduct:具体的实现创建产品的类。ConcreteCreator:实现了具体的工厂方法用来实例化具体的产品对象。

工厂模式:就是一个专门的类用来创建其他对象。工厂类中的方法必须返回一个对象,简单理解就是类的实例化交个一个单独的工厂类。如:

class MyObject{
  //对象将从工厂中返回
}
class MyFactory{
  public static function factory(){
    //返回对象的一个新实例
    return new MyObject();
  }
}

这样我们可以使用工厂类的方法返回指定类的实例:

$instance = MyFactory::factory();

示例代码一:

个人事务管理
//产品抽象类
abstract class ApptEncoder {
    abstract function encode();
}
//博客预约
class BloggsApptEncoder extends ApptEncoder {
    function encode() {
        return "Appointment data encoded in BloggsCal format
";
    }
}
//日历预约
class MegaApptEncoder extends ApptEncoder {
    function encode() {
        return "Appointment data encoded in MegaCal format
";
    }
}

class CommsManager {//工厂类
    const BLOGGS = 1;
    const MEGA = 2;
    private $mode = 1;

    function __construct( $mode ) {
        $this->mode = $mode;
    }
  //实例化具体的产品类
    function getApptEncoder() {
        switch ( $this->mode ) {
            case ( self::MEGA ):
                return new MegaApptEncoder();
            default:
                return new BloggsApptEncoder();
        }
    }
}

$comms = new CommsManager( CommsManager::MEGA );
$apptEncoder = $comms->getApptEncoder();
print $apptEncoder->encode();

  以上代码实现动机中提到的问题,这里实现的是个人事务管理的功能,使用日历和博客管理预约对象的问题。CommsManager就是工厂类,ApptEncoder 就是产品类,子类负责具体实现。getApptEncoder()方法是实例化出具体对象的方法。

示例代码二:

  图像对象工厂:不同的图像文件有不同的文件格式,因此应该为每一个类型的图像创建一个类,包含在单独的文件中。图像有获取高度宽度和原始图像数据的方法,但是他们的具体实现又不同。可以使用接口,定义公共功能,并创建工厂模式使API简单易用。

图像对象工厂
interface IImage{
  function getHeight();
  function getWidth();
  function getData();
}

//PNG处理方式具体类
class Image_PNG implements IImage{
  private $_width,$_height,$_data;

  public function __construct($file){
    $this->_file = $file;
    $this->_parse();
  }
  private function _parse(){
  //完成PNG的解析工作,并填充$_width,$_height,$_data;
  }
  public function getWidth(){
    return $this->_width;
  }
  public function getHeight(){
    return $this->_height;
  }
  public function getData(){
    return $this->_data;
  }
}

//JPEG 处理方式具体类
class Image_JPEG implements IImage{
  private $_width,$_height,$_data;

  public function __construct($file){
    $this->_file = $file;
    $this->_parse();
  }
  private function _parse(){
  //完成JPEG的解析工作,并填充$_width,$_height,$_data;
  }
  public function getWidth(){
    return $this->_width;
  }
  public function getHeight(){
    return $this->_height;
  }
  public function getData(){
    return $this->_data;
  }
}

//工厂函数
Class ImageFactory{
  public static function factory($file){
    $pathParts = pathinfo($file);
    switch(strtolower($pathParts['extension'])){
      case 'jpg':
        $ret = new Image_JPEG($file);
        break;
      case 'png':
        $ret = new Image_PNG($file);
        break;
      default:
        //有问题
    }
    if($ret instanceof IImage){
      return $ret;
    }else{
      //有问题
    }
  }
}
 //此时可以用如下的方法调用工厂处理图片:
$image = ImageFactory::factory('/path/to/free.jpg');//生成实例化对象
//获取图像的宽度
echo $image->getWidth();

示例代码三:

数据库链接
interface IDatabaseBindings{//接口
  public function userExists($email);
}
//PGSQL数据库类
class PGSQL implements IDatabaseBindings{

  protected $_connection;

  public function __construct(){
    $this->_connection = pg_connect('dbname=example_db');
  }
  public function userExists($email){
    $emailEscaped = pg_escape_string($email);
    $query = "select 1 from users where email = '".$emailEscaped."'";
    if($result = pg_query($query,$this->_connecion)){
      return (pg_num_rows($result)>0) ? true :false;
    }else{
      return false;
    }
  }
}

//MYSQL数据库类
class MYSQL implements IDatabaseBindings{

  protected $_connection;

  public function __construct(){
    $this->_connection = mysql_connect('dbname=example_db');
  }
  public function userExists($email){
    $emailEscaped = mysql_real_escape_string($email);
    $query = "select 1 from users where email = '".$emailEscaped."'";
    if($result = mysql_query($query,$this->_connecion)){
      return (mysql_num_rows($result)>0) ? true :false;
    }else{
      return false;
    }
  }
}

class DatabaseFactory{
  public static function factory(){
    $type = loadtypefromconfigfile();
    switch($type){
      case 'PGSQL':
        return new PGSQL():
        break;
      case 'MYSQL':
        return new MYSQL():
        break;

      }
  }
}
//使用
$db = DatabaseFactory::factory();
$db->userExists('person@example.com');

  调用这个工厂方法做链接数据库的方法处理的时候,返回的总是符合要求的数据库操作类。这样,在改变数据库查询的时候,只需要调用这个工厂就可以动态实现了。通过工厂方法,动态返回符合要求的实例化好的对象,在实现层不需要处理具体的数据库实现。

  为什么要用工厂方法?首先从上面的图片代码可以看到,在使用工厂的方式后,客户端代码根本不需要去管图片的格式了,直接交个工厂类取区分处理,而只要写处理的方法,这些方法都是接口实现的,即所有的处理方法名都是一样的了,只不过具体过程不同而已,因此我们只要在实现的时候调用方法,其他的实现交个工厂去筛选吧!

原文地址:https://www.cnblogs.com/colorstory/p/2644308.html