工厂方法模式

将模板方法模式用于生成实例,模板方法模式就演变为工厂方法模式。但是也失去了模板方法模式最核心的概念:父类中的 templateMethod 定义了不可变的处理流程(核心算法),不允许子类重写该方法。

类图

2024061016361372.png

简化模板方法

把模板方法中的示例代码进行简化,如下:

<?php
 
// 抽象工厂
abstract class AbstructClass
{
abstract public function step1();
final public function templateMethod()
{
$this->step1();
}
}
 
// 具体工厂1
class A extends AbstructClass
{
public function step1()
{
echo "A::step1\n";
}
}
 
// 具体工厂2
class B extends AbstructClass
{
public function step1()
{
echo "B::step1\n";
}
}
 
// 客户端
function client(AbstructClass $obj)
{
//
}
 
 
$a = new A();
$b = new B();
 
client($a);
client($b);

此时,上述代码已经实现了工厂方法模式,A 类和 B 类就是两个具体的工厂。但是该工厂创建的是 void,因为 step1() 没有返回值。

继续改造代码

创建 string 的工厂

createProduct 可以返回(创建)不同类型的数据,此处把返回值限定为一个字符串(可以理解为一个创建字符串的工厂),具体的创建逻辑交给子类负责。

<?php
 
// 抽象工厂
abstract class StringFactory
{
abstract public function createProduct(): string;
}
 
// 具体工厂1
class ShortStringFactory extends StringFactory
{
public function createProduct(): string
{
return 'Short string';
}
}
 
// 具体工厂2
class LongStringFactory extends StringFactory
{
public function createProduct(): string
{
return 'Long string';
}
}
 
// 客户端
function client(StringFactory $obj): string
{
return $obj->createProduct();
}
 
$shortStringFactory = new ShortStringFactory();
$longStringFactory = new LongStringFactory();
 
$shortString = client($shortStringFactory);
$longString = client($longStringFactory);
 
echo $shortString . PHP_EOL;
echo $longString . PHP_EOL;

和模板方法不同,由于不需要在父类中规定算法流程,所以上述的 StringFactory 可以用接口来代替。

把 string 产品化

各种教材上所说的:工厂方法模式主要用于定义一个接口用于创建对象,但让子类来决定哪个类进行实例化。此时可以把 string 变成一个类(即所谓的产品),即 ShortStringLongString

<?php
 
// 抽象产品
interface MyString
{
public function getValue(): string;
}
 
// 具体产品1
class ShortString implements MyString
{
public function getValue(): string
{
return 'Short string';
}
}
 
// 具体产品2
class LongString implements MyString
{
public function getValue(): string
{
return 'Long string';
}
}
 
// 抽象工厂
abstract class StringFactory
{
abstract public function createProduct(): MyString;
}
 
// 具体工厂1
class ShortStringFactory extends StringFactory
{
public function createProduct(): MyString
{
return new ShortString();
}
}
 
// 具体工厂2
class LongStringFactory extends StringFactory
{
public function createProduct(): MyString
{
return new LongString();
}
}
 
// 客户端
function client(StringFactory $obj): MyString
{
return $obj->createProduct();
}
 
$shortStringFactory = new ShortStringFactory();
$longStringFactory = new LongStringFactory();
 
$shortString = client($shortStringFactory);
$longString = client($longStringFactory);
 
echo $shortString->getValue() . PHP_EOL;
echo $longString->getValue() . PHP_EOL;

创建 Button 产品的工厂

这是另一示例,此处的产品是 Button。这个示例中使用接口作为抽象工厂。

<?php
 
// 抽象产品
interface Button
{
public function render();
}
 
// 具体产品1
class WinButton implements Button
{
public function render()
{
return "Render a button in Windows style.";
}
}
 
// 具体产品2
class MacButton implements Button
{
public function render()
{
return "Render a button in macOS style.";
}
}
 
// 抽象工厂
abstract class ButtonFactory
{
abstract public function createButton(): Button;
}
 
// 具体工厂1
class WinButtonFactory extends ButtonFactory
{
public function createButton(): Button
{
return new WinButton();
}
}
 
// 具体工厂2
class MacButtonFactory extends ButtonFactory
{
public function createButton(): Button
{
return new MacButton();
}
}
 
// 客户端
function client(ButtonFactory $factory)
{
return $factory->createButton();
}
 
 
$winButtonFactory = new WinButtonFactory();
$winButton = client($winButtonFactory);
 
$macButtonFactory = new MacButtonFactory();
$macButton = client($macButtonFactory);

如果在上述代码的基础上,引入另一个产品 checkbox,则产品一共有:WinButtonMacButtonWinCheckboxMacCheckbox。对于这种情况,需要引入抽象工厂模式