zoukankan      html  css  js  c++  java
  • 【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');

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

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

  • 相关阅读:
    HDU 4782 Beautiful Soup (模拟+注意细节)
    Linux 简单socket实现UDP通信
    Linux 简单socket实现TCP通信
    HDU 1698 Just a Hook(线段树区间覆盖)
    HDU 1271 整数对(思路题)
    HDU 2222 Keywords Search (AC自动机模板题)
    Windows平台使用Gitblit搭建Git服务器图文教程
    Git克隆
    移动端布局,div按比例布局,宽度为百分比,高度和宽度一样,即让div为正方形
    calc()问题
  • 原文地址:https://www.cnblogs.com/colorstory/p/2644308.html
Copyright © 2011-2022 走看看