Я хочу поговорить о паттернах проектирования в php в нескольких следующих статьях. Мне очень нравится, как развивается язык, поэтому я буду приводить примеры из последних нововведений php 8.
Синглтон
Иногда нам нужен только один экземпляр какого-то класса в разных местах кода. Например, когда мы создаем веб-приложение, обычно ему требуется подключение к базе данных, и было бы действительно хорошей идеей создать только одну копию подключения и использовать ее во всех файлах, классах, функциях. Паттерн, который поможет нам в этом — Singleton — один из самых популярных и простых паттернов проектирования.
Давайте посмотрим, как мы можем реализовать подключение к базе данных классов и ведение журнала с одним экземпляром во всех объектах:
class Singleton
{
protected static self|null $instance = null;
final private function __construct(){}
final protected function __clone(){}
final protected function __wakeup(){}
public static function getInstance(): static
{
if (static::$instance === null) {
static::$instance = new static;
}
return static::$instance;
}
}
class Database extends Singleton
{
public function connect()
{
// ...
}
}
class Logger extends Singleton
{
private $connection;
public function settings($connection = null)
{
$this->connection = $connection ?? '/var/logs/filename.log';
}
public function error(string $message)
{
// ...
}
public function warn(string $message)
{
// ...
}
}
Теперь мы будем использовать логгер для записи логов в таблицу базы данных. Для этого нам понадобится подключение к базе данных и установка его методом settings:
$db = Database::getInstance();
$db->connect();
$logger = Logger::getInstance();
$logger->settings($db);
$logger->error('Something wrong');
Multiton
Но что если нам нужно больше одного экземпляра логгера, потому что некоторые сообщения нужно записывать в файл, некоторые отправлять по электронной почте? Или у нас могут быть другие причины. Для этого мы воспользуемся шаблоном Multiton. Multiton очень похож на предыдущий паттерн, но с массивом экземпляров класса.
class Multiton
{
protected static array|null $instance = null;
final private function __construct(){}
final protected function __clone(){}
final protected function __wakeup(){}
public static function getInstance(int|string $key): self
{
if (!array_key_exists($key, self::$instance)) {
self::$instance[$key] = new self;
}
return self::$instance[$key];
}
}
class Logger extends Multiton
{
private array $settings;
public function setSettings(array $settings)
{
// ...
}
public function error(string $message)
{
// ...
}
public function warn(string $message)
{
// ...
}
}
Сделаем два логгера с разными настройками для сохранения логов в файлы и в базу данных. Я не буду подробно описывать установку настроек и запись в файл/базу данных, так как это не важно для паттерна.
$fileLogger = Logger::getInstance('file');
$fileLogger->setSettings([
//...
]);
$fileLogger->error('Error text');
$dbLogger = Logger::getInstance('database');
$dbLogger->setSettings([
//...
]);
$dbLogger->error('Error will write in Database');