二、生产对象的工厂--工厂方法模式
1、只会贴牌的工厂--简单工厂
如果我们的网站上有电影的介绍,同时下面有电影的bt种子,那我们可能需要看到电影页面的浏览情况和种子的下载情况(很多电影你看了介绍并不会下载,所以这两个数字是不同的)。那么我们可能会写两个类来分别获取统计的结果。
例:2_1
<?php
//电影介绍的处理类
class MovieView
{
public function getTimes()
{
//返回电影介绍的浏览次数
}
}
//电影种子下载的处理类
class MovieBtDown
{
public function getTimes()
{
//返回电影种子的下载次数
}
}
$movieView = new MovieView();
echo $movieView->geTimes();
$movieBtDown = new MovieBtDown();
$movieBtDown->getTimes();
?>
但是这样就意味着用的时候,完全要清楚在处理那个事务,也就是new哪个类的对象。
那么如果我们后面再加一个功能,统计电影被评分,我们就要再追加new语句。
而且往往看统计的都是在用户界面(UI),一般通过接收参数变量,来确定操作的是哪个功能。
其实我们需要的是把参数变量变成new语句,获得需要的对象。
这就好像一家贴牌的工厂,从进去参数(类名),出来直接就是对象,没有其他的步骤。
例:2_2
<?php
//电影介绍的处理类
class MovieView
{
public function getTimes()
{
//返回电影介绍的浏览次数
}
}
//电影种子下载的处理类
class MovieBtDown
{
public function getTimes()
{
//返回电影种子的下载次数
}
}
//电影工厂
class MovieFactory
{
//获取电影处理类的对象,参数为类名
public function getMovieObject($className)
{
return new $className;
}
}
//获取电影工厂
$movieFactory = new MovieFactory();
//电影介绍
$what = 'MovieView';
$movieObject = $movieFactory->getMovieObject($what);
$movieObject->getTimes();
//电影种子下载
$what = 'MovieBtDown';
$movieObject = $movieFactory->getMovieObject($what);
$movieObject->getTimes();
?>
2、安排人员审核订单
我们为了安全,或者防止错误,希望传过来的参数是现存的类的名字,就好像订单一般需要人员来审核。
我们可以加个if,我们可以改一下MovieFactory的getMovieObject方法。
例:2_3
<?php
//电影工厂
class MovieFactory
{
//获取电影处理类的对象,参数为类名
public function getMovieObject($className)
{
if ($className==='MovieView' || $className==='MovieBtDown') {
return new $className;
}
}
}
3、买了生产设备的新工厂--工厂方法模式
其实往往我们不光是需要得到一些对象,在new之前或之后,我们还需要执行一些行为,那么此时,简单的将参数转换成类名,就显得不太够。
这时候,也有几种处理方法,比如把这些步骤都塞进要new的类的构造方法__construct。
但是这样会使得构造方法很臃肿,而且出现了一个问题,比如下载BT种子,有些网站是要扣除积分的,但是如果是管理员下载,不应扣除积分,因为管理员要检查每个种子。
同样,上传种子的时候,可以给用户添加积分,而管理员,可以不必接受积分的处理。
这样构造方法就非常复杂,但种子的下载和上传本身是与这些工作无关的。
那么我们就难以用贴牌工厂的简单方法,我们需要生产设备了。
工厂方法模式(Factory Method):
例:2_4
<?php
//BT种子的接口
interface BT
{
public function process();
}
//BT种子的下载处理类
class BTdown implements BT
{
public function process()
{
//输出BT种子
echo '输出BT种子';
}
}
//BT种子的上传处理类
class BtUpload implements BT
{
public function process()
{
//保存上传的BT种子
echo '保存上传的BT种子';
}
}
//生产BT种子处理对象的设备的接口
interface BTcreator
{
public function createBTObject();
}
//生产BT种子下载处理对象的设备
class BTdownCreator implements BTcreator
{
public function createBTObject()
{
/**
* 如果不是管理员,送10点积分
*/
//然后返回BT种子下载处理对象
return new BTdown();
}
}
//生产BT种子上传处理对象的设备
class BTUploadCreator implements BTcreator
{
public function createBTObject()
{
/**
* 如果不是管理员,送10点积分
*/
//然后返回BT种子下载处理对象
return new BtUpload();
}
}
//生产BT种子处理对象的工厂
class BtFactory
{
//获取电影处理类的对象,参数为类名
public function getBtObject($name)
{
switch ($name) {
case 'BTdown':
$creator = new BTdownCreator();
break;
case 'BtUpload':
$creator = new BtUploadCreator();
break;
}
return $creator->createBTObject();
}
}
//获取bt工厂
$btFactory = new BtFactory();
//BT种子的下载处理
$what = 'BTdown';
$btObject = $btFactory->getBtObject($what);
$btObject->process();
//BT种子的上传处理
$what = 'BtUpload';
$btObject = $btFactory->getBtObject($what);
$btObject->process();
?>
上面的代码就可以看到,生产种子的处理对象时,由生产设备类来处理多余的事务,工厂本身也不受影响。
思考:
工厂是个不错的模式,但是很多时候,用简单的贴牌工厂就可以完成工作。
如果用工厂方法模式,那么就需要设计设备类,是否有必要用,这是需要考虑的,否则就多花了精力,但是没得到实际效果。