【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\n"; } } //日历预约 class MegaApptEncoder extends ApptEncoder { function encode() { return "Appointment data encoded in MegaCal format\n"; } } 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');
调用这个工厂方法做链接数据库的方法处理的时候,返回的总是符合要求的数据库操作类。这样,在改变数据库查询的时候,只需要调用这个工厂就可以动态实现了。通过工厂方法,动态返回符合要求的实例化好的对象,在实现层不需要处理具体的数据库实现。
为什么要用工厂方法?首先从上面的图片代码可以看到,在使用工厂的方式后,客户端代码根本不需要去管图片的格式了,直接交个工厂类取区分处理,而只要写处理的方法,这些方法都是接口实现的,即所有的处理方法名都是一样的了,只不过具体过程不同而已,因此我们只要在实现的时候调用方法,其他的实现交个工厂去筛选吧!
posted on 2014-06-22 15:02 color_story 阅读(238) 评论(0) 编辑 收藏 举报