在讲解工厂模式之前,我们先来探讨一些问题,研究是为什么会出现工厂模式的,工厂模式有什么优缺点。
以超人为例子:有一个超人,超人一定有对种超能力;
于是我们建立一个超人类
namespace Factory;
class Superman
{
}
同时创建超能力类并给超人添加超能力;
namespace Factory;
class Flight
{
/**
* 能力的名称
*
* @var
*/
protected $name;
/**
* 能力值
*
* @var
*/
protected $ability;
public function __construct($name, $ability)
{
$this->name = $name;
$this->ability = $ability;
}
}
class Force
{
//该类的属性和方法
}
class Jog
{
//该类的属性和方法
}
给超人添加超能力
namespace Factory;
class Superman
{
public function __construct()
{
$power_class = new Power('超能力', 1000);
$force_class = new Force();
$jog_class = new Jog();
}
}
通过上面的代码可以得到:
如果上面的修改出现在多个不同的地方,那么我们就会花费很多时间或精力去修改这个东西。所以我们可以引出工厂模式,去解决这些问题。
问题1:
解决方案:简单工厂模式
namespace Factory;
class SimpleFactory
{
protected $power_type = [
'force' => __NAMESPACE__ . '\Force',
];
/**
* 添加超能力
* @param string $type 超能力类型key
* @return 超能力的实例化对象
* @throws \Exception
*/
public function addPower($type)
{
if (!array_key_exists($type, $this->power_type)) {
throw new \Exception("超能力不存在");
}
$class_name = $this->power_type[$type];
return new $class_name;
}
}
namespace Factory;
/**
* 具体超能力类
* Class Force
* @package Factory
*/
class Force extends SimpleFactory
{
public function setPower()
{
echo "超能力:力量" . PHP_EOL;
}
}
addPower('force')->setPower();
} catch (Exception $e) {
echo $e->getMessage();
}
总结
问题2: 如果不想在调用的时候new容器
解决:使用静态工厂模式
namespace Factory;
class SimpleFactory
{
protected $power_type = [
'force' => __NAMESPACE__ . '\Force',
];
/**
* 添加超能力(将方法改成静态)
* @param string $type 超能力类型key
* @return 超能力的实例化对象
* @throws \Exception
*/
public static function addPower($type)
{
if (!array_key_exists($type, self::power_type)) {
throw new \Exception("超能力不存在");
}
$class_name = self::power_type[$type];
return new $class_name;
}
}
总结
与简单工厂类似,该模式用于创建一组相关或依赖的对象,不同之处在于静态工厂模式使用一个静态方法来创建所有类型的对象,该静态方法通常是 factory 或 build。
因为上面的超能力只有一个,所以不会出现调用超能力方法不一样的情况;如果现在超能力有多个的化,我们最后规范一下超能力调用方法接口。因此引入超能力的接口类。(也可以理解成面向接口开发,这样别人就能够更加方便调用)
namespace Factory;
/**
* 超能力接口
* Interface PowerInterface
* @package Factory
*/
interface PowerInterface
{
public function setPower();
}
namespace Factory;
class Force implements PowerInterface
{
public function setPower()
{
echo "超能力:力量" . PHP_EOL;
}
}
namespace Factory;
class Jog implements PowerInterface
{
public function setPower()
{
echo "超能力:跑步";
}
}
namespace Factory;
class SimpleFactory
{
protected $module; //超能力类对象
/**
* 这里使用依赖注入的方式
* SimpleFactory constructor.
* @param PowerInterface $type
*/
public function __construct(PowerInterface $type)
{
$this->module = $type;
}
/**
* 添加超能力类
*/
public function addPower()
{
$this->module->setPower();
}
}
问题3:
解决:
首先对工厂进行操作
namespace Factory;
/**
* 工厂方法抽象类
* Class FactoryMethod
* @package Factory
*/
abstract class FactoryMethod
{
const JOG = 1;
const FORCE = 2;
/**
* 子类实现启动超能力方法
* @param $type
* @return mixed
*/
abstract protected function activate($type);
/**
* 添加超能力
* @param $type
* @return PowerInterface a new power
*/
public function addPower($type)
{
$obj = $this->activate($type);
$obj->setPower();
return $obj;
}
}
namespace Factory;
use Exception;
/**
* 工厂类1
* Class FactoryOne
* @package Factory
*/
class FactoryOne extends FactoryMethod
{
protected function activate($type)
{
switch ($type) {
case parent::JOG :
return new Jog();
break;
case parent::FORCE :
return new Force();
break;
default :
throw new Exception("this type is not a valid power");
}
}
}
namespace Factory;
use Exception;
/**
* 工厂类2
* Class FactoryTwo
* @package Factory
*/
class FactoryTwo extends FactoryMethod
{
public function activate($type)
{
switch ($type) {
case parent::JOG :
return new Jog();
break;
case parent::FORCE :
return new Force();
break;
default :
throw new Exception("this type is not a valid power");
}
}
}
工厂1和工厂2继承工厂方法抽象类,实现创建超能力(工厂里边存放实现超能力的对象),所以工厂实际上就是,把创建对象的过程封装起来,随时可以产生一个新的对象。
然后就是对超能力的操作
namespace Factory;
/**
* 超能力接口
* Interface PowerInterface
* @package Factory
*/
interface PowerInterface
{
public function setPower();
}
namespace Factory;
class Force implements PowerInterface
{
public function setPower()
{
echo "超能力:力量" . PHP_EOL;
}
}
namespace Factory;
class Jog implements PowerInterface
{
public function setPower()
{
echo "超能力:跑步";
}
}
到这里工厂方法模式已经完成了,现在我们先继续把创建超人的代码完成。
最后创建超人类Superman.php
namespace Factory;
class Superman
{
protected $module;
/**
* Superman constructor.
* @param FactoryMethod $factory
*/
public function __construct(FactoryMethod $factory)
{
$this->module = $factory;
}
/**
* 创建具有某种超能力的超人
* @param $type
*/
public function addSuperman($type)
{
$this->module->addPower($type);
}
}
现在我们创建超人就非常方便了
addSuperman(1);
//利用工厂2 创建超人(超能力是大力)
$superman_2 = new Superman(new FactoryTwo());
$superman_2->addSuperman(2);
定义多个工厂,每个工厂生产不同的超人的超能力实例,支持增加超能力类型,或工厂生产哪中超能力。
创建超人的时候,我们不用关注工厂是如何创建超人的,我们只关注工厂(工厂抽象方法类)提供的接口就可以了。这样看来工厂就是将创造超能力(多个对象)的过程封装起来。
工厂方法针对每一种产品提供一个工厂类,通过不同的工厂实例来创建不同的产品实例,在同一等级结构中,支持增加任意产品(或者我可以把他理解成:将创建对象的过程封装起来,这样对于外部关注抽象类,而不是具体的类)。这个模式同时也实现了依赖倒置。
我们经常在数据库的联系、支付接口或者用户工厂的时候使用工厂模式。比如未来可能对应不同的支付网关:支付宝、财付通、网银在线等。方便未来扩展,设计成工厂模式。定一个专门生产网关接口的工厂,抽象出来,做成接口形式,让所有的子类都要实现它的接口。以后加一个支付方式,要使用哪一种支付方式,改变一下参数即可。