场景: 组装电脑。
需要做的工作:
选择所有配件,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. 容易造成层次复杂
抽象工厂的本质:
选择产品簇的实现。定义在抽象工厂里面的方法通常是有联系的,它们都是产品的某一部分,或者是相互依 赖的。如果只定义一个方法,直接创建产品,则就退化成了工厂方法了。
工厂方法的本质:
选择单个产品的实现。虽然一个类里可以有多个工厂方法,但这些方法之间是没有联系的。
何时选用抽象工厂模式: