工厂方法模式

目的

与简单工厂模式相比,工厂方法模式的优点是你可以通过子类来实现不同的对象创建方式。

对于简单的情况,这个抽象类可以只是一个接口。

这种模式是一种“真正的”设计模式,因为它实现了依赖倒置原则,也就是 SOLID 原则中的 D。

这意味着 FactoryMethod 类依赖于抽象,而不是具体的类。与简单工厂模式或静态工厂模式相比,这才是真正的技巧。

UML 类图

工厂方法模式类图

代码

Logger.php

1
2
3
4
5
6
7
8
<?php declare(strict_types = 1);

namespace DesignPatterns\Creational\FactoryMethod;

interface Logger
{
public function log(string $message);
}

StdoutLogger.php

1
2
3
4
5
6
7
8
9
10
11
<?php declare(strict_types = 1);

namespace DesignPatterns\Creational\FactoryMethod;

class StdoutLogger implements Logger
{
public function log(string $message)
{
echo $message;
}
}

FileLogger.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php declare(strict_types = 1);

namespace DesignPatterns\Creational\FactoryMethod;

class FileLogger implements Logger
{
private string $filePath;

public function __construct(string $filePath)
{
$this->filePath = $filePath;
}

public function log(string $message)
{
file_put_contents($this->filePath, $message . PHP_EOL, FILE_APPEND);
}
}

LoggerFactory.php

1
2
3
4
5
6
7
8
<?php declare(strict_types = 1);

namespace DesignPatterns\Creational\FactoryMethod;

interface LoggerFactory
{
public function createLogger(): Logger;
}

StdoutLoggerFactory.php

1
2
3
4
5
6
7
8
9
10
11
<?php declare(strict_types = 1);

namespace DesignPatterns\Creational\FactoryMethod;

class StdoutLoggerFactory implements LoggerFactory
{
public function createLogger(): Logger
{
return new StdoutLogger();
}
}

FileLoggerFactory

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php declare(strict_types = 1);

namespace DesignPatterns\Creational\FactoryMethod;

class FileLoggerFactory implements LoggerFactory
{
private string $filePath;

public function __construct(string $filePath)
{
$this->filePath = $filePath;
}

public function createLogger(): Logger
{
return new FileLogger($this->filePath);
}
}

测试

Tests/FactoryMethodTest.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<?php declare(strict_types = 1);

namespace DesignPatterns\Creational\FactoryMethod\Tests;

use DesignPatterns\Creational\FactoryMethod\FileLogger;
use DesignPatterns\Creational\FactoryMethod\FileLoggerFactory;
use DesignPatterns\Creational\FactoryMethod\StdoutLogger;
use DesignPatterns\Creational\FactoryMethod\StdoutLoggerFactory;
use PHPUnit\Framework\TestCase;

class FactoryMethodTest extends TestCase
{
public function testCanCreateStdoutLogging()
{
$loggerFactory = new StdoutLoggerFactory();
$logger = $loggerFactory->createLogger();

$this->assertInstanceOf(StdoutLogger::class, $logger);
}

public function testCanCreateFileLogging()
{
$loggerFactory = new FileLoggerFactory(sys_get_temp_dir());
$logger = $loggerFactory->createLogger();

$this->assertInstanceOf(FileLogger::class, $logger);
}
}