Purpose
The purpose of the design pattern
The good point over the SimpleFactory is you can subclass it to implement different ways to create objects.
For simple cases, this abstract class could be just an interface.
This pattern is a "real" Design Pattern because it achieves the Dependency Inversion principle a.k.a the "D" in SOLID principles.
It means the FactoryMethod class depends on abstractions, not concrete classes. This is the real trick compared to SimpleFactory or StaticFactory
UML
UML design pattern diagram
Code
Code snippets
Logger
Logger Interface - factories must adhere to this interface.
namespace DesignPatterns\Creational\FactoryMethod;
interface Logger
{
public function log(string $message);
}
LoggerFactory
LoggerFactory Interface - Concrete subclasses must adhere to this interface.
namespace DesignPatterns\Creational\FactoryMethod;
interface LoggerFactory
{
public function createLogger(): Logger;
}
FileLogger
The FileLogger class is a concrete subclass which implements the Logger Interface, which forces it to have a log method.
namespace DesignPatterns\Creational\FactoryMethod;
class FileLogger implements Logger
{
public function __construct(private string $filePath)
{
}
public function log(string $message)
{
file_put_contents($this->filePath, $message . PHP_EOL, FILE_APPEND);
}
}
StdOutLogger
The StdOutLogger class is a concrete subclass which implements the Logger Interface, which forces it to have a log method.
namespace DesignPatterns\Creational\FactoryMethod;
class StdoutLogger implements Logger
{
public function log(string $message)
{
echo $message;
}
}
FileLoggerFactory
The FileLoggerFactory must implement the LoggerFactory interface, which forces it to have a createLogger method. The createLogger method returns an instance of Logger.
namespace DesignPatterns\Creational\FactoryMethod;
class FileLoggerFactory implements LoggerFactory
{
public function __construct(private string $filePath)
{
}
public function createLogger(): Logger
{
return new FileLogger($this->filePath);
}
}
StdOutLoggerFactory
The StdOutLoggerFactory must implement the LoggerFactory interface, which forces it to have a createLogger method. The createLogger method returns an instance of Logger.
namespace DesignPatterns\Creational\FactoryMethod;
class StdoutLoggerFactory implements LoggerFactory
{
public function createLogger(): Logger
{
return new StdoutLogger();
}
}
Tests
Examples of creating two class instances via the Factory Method.
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);
}