php设计模式(四):抽象工厂(Abstract Factory)
- 陈大剩
- 2023-04-13 09:13:03
- 747
抽象工厂
抽象工厂(Abstract Factory)是一种创建型设计模式,它能创建一系列相关的对象,而无需指定其具体类。
问题
假设你需要开发一款组装DIY台式电脑的应用,最初只需要拼装一台台式电脑并计算出每台总部件价格即可(简单工厂方法),现在需要组装成品牌机(华硕、联想、…),对于品牌机每个部件都需要属于这个品牌。
通过工厂方法我们需要创建很多的产品和工厂类,而且你不留神,可能会给顾客组装错误的部件,如华硕的台式机组装了联想的键盘,这样如果顾客收到的部件不一样,他们会心里万只草泥马。
此外, 你也不希望在添加新部件或新品牌时修改已有代码。品牌机供应对于产品目录的更新非常频繁,你不会想在每次更新时都去修改核心代码的。
解决方法
STEP 1 建立产品
抽象工厂模式为系列中的每个部件明确声明接口(例如键盘、鼠标或显示器)。然后,确保所有部件变体都继承这些接口。例如,所有品牌的鼠标都实现 鼠标 接口;所有品牌的键盘都实现 键盘 接口,以此类推。
STEP 2 声明抽象工厂
声明抽象工厂。例如 createMouse
创建鼠标 、 createKeyboard
创建键盘 和 createDisplay
创建显示器 。 这些方法必须返回抽象产品类型,即我们之前抽取的那些接口: 鼠标 ,沙发 和 显示器 等等。
STEP 3 创建具体工厂
创建具体品牌工厂。例如 AsusComputerAbstractFactory
华硕台式电脑工厂,实现 STEP 2 声明抽象工厂 抽象工厂类接口。
STEP 4 客户端使用
客户端直接使用STEP 3 创建具体品牌工厂具体的工厂即可,无需知道部件是什么品牌。
结构
多个 interface 或者 abstract 产品基类
多个实现 interface 或者继承 abstract 的具体产品类
1个 interface 或者 abstract 工厂基类
1个实现 interface 或者继承 abstract 的具体工厂类
具体的工厂类里面有多个方法分别实例化具体的产品类
代码示例
抽象产品基类
// Keyboard.php
interface Keyboard
{
public function getPrice();
}
// Mouse.php
interface Mouse
{
public function getPrice();
}
具体产品
华硕电脑
// AsusMouse.php
class AsusMouse implements Mouse
{
public function getPrice(): float
{
return 49.9;
}
}
// AsusKeyboard.php
class AsusKeyboard implements Keyboard
{
public function getPrice(): float
{
return 149.9;
}
}
...
产品工厂类基类
// AbstractFactory.php
interface AbstractFactory
{
public function createKeyboard();
public function createMouse();
}
产品工具体厂类
// AsusComputerAbstractFactory.php
class AsusComputerAbstractFactory implements AbstractFactory
{
public function createKeyboard(): Product
{
return new AsusKeyboard();
}
public function createMouse(): Product
{
return new AsusMouse();
}
...
}
客户端类
// Client.php
// 华硕工厂类
$asusComputer = new AsusComputerAbstractFactory();
// 鼠标
$mouse = $asusComputer->createMouse();
$mousePrice = $mouse->getPrice();
// 键盘
$keyboard = $asusComputer->createKeyboard();
$keyboardPrice = $keyboard->getPrice();
...
echo $mousePrice + $keyboardPrice + ...;
UML
代码
优缺点
优点
- 你可以确保同一工厂生成的产品相互匹配。
- 你可以避免客户端和具体产品代码的耦合。
- 单一职责原则。 你可以将产品生成代码抽取到同一位置, 使得代码易于维护。
- 开闭原则。 向应用程序中引入新产品变体时, 你无需修改客
户端代码。
缺点
- 工厂需要引入很多新的子类,代码可能会因此变得更复杂。