场景: 组装电脑。
需要做的工作:
选择所有配件,CPU,主板,显卡,内存,电源等。为了简单只选择CPU,和主板的问题。
CPU :属性有品牌,型号,针脚,确定了这些才能确定具体的CPU
主板:属性有品牌,芯片组等。也只有这些确定了,才能确定具体的主板。
需要考虑各个配件之间的兼容性。cpu针角与主板提供的针角是否兼容。
装机工程师只装机,而客户负责选择配件。
不用模式的装机方案:(还是用到的简单工厂)
interface CPUApi {
public function calculate();
}
interface MainboardApi {
public function installCPU();
}
class IntelCPU implements CPUApi {
private $pins = 0;
public function __construct($pins) {
$this->pins = $pins;
}
public function calculate() {
echo 'now in Inter CPU, pins='.$this->pins;
}
}
class AMDCPU implements CPUApi {
private $pins = 0;
public function __construct($pins) {
$this->pins = $pins;
}
public function calculate() {
echo 'now in AMD CPU, pins='.$this->pins;
}
}
class GAMainboard implements MainboardApi {
private $cpuHoles = 0;
public function __construct($cpuHoles) {
$this->$puHoles = $cpuHoles;
}
public function installCPU() {
echo 'now in GAMainboard cpuHoles=' + $this->cpuHoles;
}
}
class MSIMainboard implements MainboardApi {
private $cpuHoles = 0;
public function __construct($cpuHoles) {
$this->$puHoles = $cpuHoles;
}
public function installCPU() {
echo 'now in MSIMainboard cpuHoles=' + $this->cpuHoles;
}
}
class CPUFactory {
public static function createCPUApi($type) {
switch($type) {
case '1':
$cpu = new GAMainboard(1156);
break;
case '2':
$cpu = new AMDCPU(939);
break;
}
return $cpu;
}
}
class MainboardFactory {
public static function createMainboardApi($type) {
switch($type) {
case '1':
$mainbord = new IntelCPU(1156);
break;
case '2':
$mainbord = new MSIMainboard(939);
break;
}
return $mainbord;
}
}
class ComputerEngineer {
private $cpu = null;
private $mainboard = null;
public function makeComputer($CPUApi, $Mainboard) {
//1 准备硬件
$this->prepareHardwares($CPUApi, $Mainboard);
//2 组装机器
//3 测试
//4 交付客户
}
public function prepareHardwares($cpuType, $mainboardType) {
$this->cpu = CPUFactory::createCPUApi($cpuType);
$this->mainboard = MainboardFactory::createMainboardApi($mainboardType);
$this->cpu->calculate();
$this->mainboard->installCPU();
}
}
class Client {
public static function main() {
$engineer = new ComputerEngineer();
$engineer->makeComputer(1,1);
}
}
上面的实现是有些问题:
1. CPU与主板是有关系的,需要相互匹配。
2. 只知道所需对象的接口,只不知道其具体实现,或是不知道具体使用那一个。
但上面的实现方案,是无法解决这个问题。工厂方法和简单工厂只关注单个产品的创建,CPU工厂只关注CPU,主板工厂只关注主板。这里要解决的问题是要创建一系列的产品对象,而这系列对象是构建新的对象所需要的组成部分,这一列的对象相互之间是有约束的。
解决方案:使用抽象工厂模式来解决问题;
抽象工厂模式
定义:提供一个创建一系列相关或相互依赖对象的接口,而无需指定他们的具体实现类。
// 对以上代码进行改造,加入抽象工厂的定义
interface AbstractFactory {
public function createCPUApi();
public function createMainboard();
}
class Schema1 implements AbstractFactory {
public function createCPUApi() {
return new IntelCPU(1156);
}
public function createMainboard() {
return new GAMainboard(1156);
}
}
class Schema2 implements AbstractFactory {
public function createCPUApi() {
return new AMDCPU(939);
}
public function createMainboard() {
return new MSIMainboard(939);
}
}
//修改工程师类
class ComputerEngineer2 {
private $cpu = null;
private $mainboard = null;
public function makeComputer(AbstractFactory $schema){
$this->prepareHardwares($schema);
}
public function prepareHardwares(AbstractFactory $schema){
$this->cpu = $schema->createCPUApi();
$this->mainboard = $schema->createMainboard();
$this->cpu->calculate();
$this->mainboard->installCPU();
}
}
class Client2 {
public static function main() {
$engineer = new ComputerEngineer();
$schema = new Schema1();
$engineer->makeComputer($schema);
}
}
模式讲解:
功能: 为一系列相关对象或相互依赖的对象创建一个接口。需要注意的是这个接口内的方法不是随意堆砌的,而是一系列相关或相互依赖的方法。从某种意义上讲,抽象工厂是现代战争产品系列。
实现成接口: AbstractFactory 通常为接口,不要被名字误导,以为是抽象类。当然如果提供公共的功能,也未偿不可,但一般不这样做。
使用工厂方法: AbstractFactory 定义了创建产品所需要的接口,具体的实现是在实现类里面,通常在实现类里 面就需要多种更具体的实现。所以 AbstractFactory定义的创建产品的方法可以看成是工厂方法,而这些工厂方法的具体实现延迟到了具体的工厂里面,也就是说使用工厂方法来实现抽象工厂。
切换产品: 抽象工厂定义的一系列对象是相互关联的,这些产品就构成了产品簇,也就是抽象工厂定义了一个产品簇。切换产品簇时,就只要提供一个不同的抽象工厂实现就可以了。
抽象工厂模式的调用顺序:
1. 客户端 创建具体工厂2. 工厂创建产品A,B。3. AB分别调用A,B方法。
可扩展的工厂:
当前如果要增加内存时,就需要在抽象工厂方法里面创建添加内存的方法。当抽象工厂发生改变所有的具体实现都要发生改变。如上不灵活。
改进方式:抽象工厂只需要一个方法,给这个方法一个参数,通过这个参数来判断具体要创建什么产品对象,由于只有一个方法,所以返回类型不在是具体的一个产品了,只是能所有产品都继承或实现的一个类型。
改造抽象工厂:
//继承改造上面的不灵活
interface AbstractFactory2 {
public function createProduct($type);
}
class Schema2_1 implements AbstractFactory2 {
public function createProduct($type) {
$object = null;
switch($type) {
case '1':
$object = new IntelCPU(1156);
break;
case '2':
$object = new GAMainboard(1156);
break;
}
return $object;
}
}
class Schema2_2 implements AbstractFactory2 {
public function createProduct($type) {
$object = null;
switch($type) {
case '1':
$object = new AMDCPU(939);
break;
case '2':
$object = new MSIMainboard(939);
break;
}
return $object;
}
}
class ComputerEngineer3 {
private $cpu = null;
private $mainboard = null;
public function makeComputer(AbstractFactory2 $schema){
$this->prepareHardwares($schema);
}
public function prepareHardwares(AbstractFactory2 $schema){
$this->cpu = $schema->createProduct(1);
$this->mainboard = $schema->createProduct(2);
$this->cpu->calculate();
$this->mainboard->installCPU();
}
}
//体现以上试带来的灵活,增加新产品,内存
interface MemoryApi {
function cacheData();
}
class HyMemory implements MemoryApi {
public function cacheData() {
echo '正在使用现代内存';
}
}
class Schema2_3 implements AbstractFactory2 {
public function createProduct($type) {
$object = null;
switch($type) {
case '1':
$object = new IntelCPU(1156);
break;
case '2':
$object = new GAMainboard(1156);
break;
case '3':
$object = new HyMemory();
break;
}
return $object;
}
}
class ComputerEngineer4 {
private $cpu = null;
private $mainboard = null;
private $memory = null;
public function makeComputer(AbstractFactory2 $schema){
$this->prepareHardwares($schema);
}
public function prepareHardwares(AbstractFactory2 $schema){
$this->cpu = $schema->createProduct(1);
$this->mainboard = $schema->createProduct(2);
$this->memory = $schema->createProduct(3);
$this->cpu->calculate();
$this->mainboard->installCPU();
$this->memory->cacheData();
}
}
class Client3 {
public static function main() {
$engineer = new ComputerEngineer();
$schema = new Schema2_3();
$engineer->makeComputer($schema);
}
}
抽象工厂模式和DAO
优点:
1. 分享接口与实现
2. 使得切换产品簇变得容易
缺点:
1. 不太容易扩展新产品
2. 容易造成层次复杂
抽象工厂的本质:
选择产品簇的实现。定义在抽象工厂里面的方法通常是有联系的,它们都是产品的某一部分,或者是相互依 赖的。如果只定义一个方法,直接创建产品,则就退化成了工厂方法了。
工厂方法的本质:
选择单个产品的实现。虽然一个类里可以有多个工厂方法,但这些方法之间是没有联系的。
何时选用抽象工厂模式: