PHP设计模式:工厂模式之抽象工厂
发布于: August 13, 2009, 9:05 pm 分类: PHP/MySQL 作者: Saturn
上一篇中,我主要记录了工厂模式之一的简单工厂在PHP中的实现,这篇文章主要总结工厂模式的另一种形态:抽象工厂。
简单工厂中,工厂的角色是负责实例化具体对象,而在实际应用中这种情况却不很实用,主要原因是如果我们希望增加或去除这个工厂所能实例化的某个对象,则需要对工厂类进行直接修改,这显然违背了低耦合的宗旨。对于上一篇文章的例子来说,那就是:如果我们需要添加那个汽车工厂所能生成的汽车品牌,则需要修改工厂类的判断逻辑。
如何尽可能的降低耦合,从而达到非侵入式的设计逻辑?这里的非侵入式一次,是我从Spring中借用过来的概念,通俗来讲,就是去除和添加某个工厂的产品对象不影响整个程序的架构。
由此就引入了抽象工厂的思想。抽象工厂将面向接口编程的思想演绎的淋漓尽致,它的核心思想是:抽象工厂仅提供一个产品簇(同一类别的所有对象的接口抽象)的通用接口,而将实例化对象的任务交给遵循其标准的具体工厂来完成;具体工厂还可能生产出多个产品,此时还需要对产品定义一个接口,供具体工厂来生产具体产品。
举个例子,还是关于生产汽车的。汽车工厂经过一段时间的努力,现在已经发展装大了,于是做了两个决定:第一个决定是将生产汽车的过程分拆成两个阶段,分别是生成轮胎和除去轮胎的其他部分。另一个决定是,决定为每个汽车品牌单独建立一个汽车工厂。那么如何管理这些工厂,使他们既遵循第一个决定,又不相互干扰?解决办法就是:汽车总厂提供生产汽车的通用流程(第二条),这个流程两个子工厂都需要严格执行。同时,将具体生产的过程交给两个具体工厂,分别是Benz工厂和Audi工厂。这就是抽象工厂的设计思想的概念。
总结一下,一个严格的抽象工厂模式应该包括以下几个部分:
- 抽象工厂 Abstract Factory : 声明生成抽象产品的方法
- 具体工厂 Concrete Factory:执行抽象工厂中定义的方法,生产一个具体产品(实例化一个具体对象)
- 抽象产品 Abstract Product :产品接口
- 具体产品 Product:实现产品接口
- 产品客户 Client:客户端,通过具体工厂得到一个具体产品
理解了以上几部分的关系,要在PHP中实现就相对简单了,代码如下:
<?php
/***************************************************************************
* 抽象工厂模式的PHP实现
*
* @Author : Saturn
* @contact: http://www.cnsaturn.com/
***************************************************************************/
//抽象工厂,负责定义具体工厂要执行的方法
abstract class AbstractCarFactory
{
abstract public function produceTyres();
abstract public function produceOtherParts();
}
//生产Benz的具体工厂,负责实例化产品对象
class BenzFactory extends AbstractCarFactory
{
public function produceTyres()
{
return new BenzTyres();
}
public function produceOtherParts()
{
return new BenzOtherParts();
}
}
//另一个负责生产Audi的具体工厂
class AudiFactory extends AbstractCarFactory
{
public function produceTyres()
{
return new AudiTyres();
}
public function produceOtherParts()
{
return new AudiOtherParts();
}
}
//抽象产品,定义产品接口
class Tyres
{
function __construct()
{
echo get_class($this).' is produced!' . "\n";
}
}
//抽象产品,定义产品接口
class OtherParts
{
function __construct()
{
echo get_class($this).' is produced!' . "\n";
}
}
//具体产品,实现产品接口
class AudiTyres extends Tyres
{
function __construct()
{
parent::__construct();
}
}
//具体产品,实现产品接口
class AudiOtherParts extends Tyres
{
function __construct()
{
parent::__construct();
}
}
//具体产品,实现产品接口
class BenzTyres extends Tyres
{
function __construct()
{
parent::__construct();
}
}
//具体产品,实现产品接口
class BenzOtherParts extends Tyres
{
function __construct()
{
parent::__construct();
}
}
//客户端 test
$brand = 'Benz';
$placeHolderFactory = $brand.'Factory';
$audi = new $placeHolderFactory;
$audi->produceTyres();
$audi->produceOtherParts();
//输出如下:
//BenzTyres is produced!
//BenzOtherParts is produced!
?>
需要注意到是,这里我用来定义工厂使用的是abstract类,而上一篇文章中使用的是interface;二者均可用于定义一个工厂。
到这里,工厂模式的介绍笔记就算完结了。但有个概念仍然在我脑袋里盘旋,那就是耦合。
在实际应用中,就算是严格的遵循这种抽象工厂模式,有时候也不能有效的降低耦合。如上例,如果我再讲汽车的其他的部件作为一个产品分离出来,则需要同时修改抽象工厂以及具体工厂……所以,具体应用还需依情况而定。
在撰写这篇文章的过程中,我再次体会到了Spring中Dependency Injection的强大之处,DI是Spring的灵魂。依赖注入完美的解决了上面我考虑的问题,你要做的仅仅是配置一个XML文件,或者添加一个autowired的annotation到注入的方法上,再借助Spring这个框架,就完成了以上将工厂硬编码在程序中的功能。
在开发Enterprise App上,JAVA强大的让人可怕。Fantastic!
回应此文
你也可以选择引用此文章.