Сегодня мы обсудим паттерн Special Case. Данный паттерн представляет собой гибкий способ обхода ограничений возникающих при появлении, т.н. специальных случаев.
Описание
При проектировании объектно-ориентированной системы часто возникают ситуации, когда нужно обработать какой-либо особый случай, который выбивается из обычного цикла работы. При этом возникает необходимость проверять возникновение такой ситуации по всей системе, что само-собой приводит к неприятным последствиям. Примером такого случая может быть возвращение null вместо объекта при отсутствии или некорректности каких-либо данных. При этом приходится после получения ссылки на такой объект проверять равенство его нулю и отдельным образом обрабатывать каждый такой случай. Это очевидно приводит к жуткому дублированию кода, что неминуемо будет приводить к копи-пасту и соответственно множеству ошибок и ограничений.
Как вы наверное уже догадались справится с данной проблемой нам поможет паттерн Special Case. Суть данного паттерна заключается в замене таких особых случаев на специальный объект, имеющий тот же тип и возврат его вызывающему коду. Полученный объект будет иметь точно такой же интерфейс, как и у обычного класса и соответственно нам не потребуется выполнять никаких скучных проверок. Заменив возврат особых случаев на специализированный объект мы значительно улучшаем архитектуру приложения, т.к. вызывающий код не будет заботится о том каким образом создаётся объект и от чего зависит его нормальная инициализация.
Пример
Самый очевидным примером данного шаблона проектирования может служить иерархия классов пользователей. В данном случае особым случаем будет пользователь гость, т.к. он не представлен в БД, т.к. другие пользователи. Рассмотрим следующий упрощенный пример:
<?php
// ...
class User{
protected $user_info;
public function __construct($id_user) {
// Вытягиваем информацию о пользователе из БД
}
public function getName(){
return $this->user_info['name'];
}
public function getEmail(){
return $this->user_info['name'];
}
public function getStatus(){
return $this->user_info['status'];
}
/**
* Проверка прав доступа пользователя
* @param string $action
* @return bool
*/
public function checkAccessRights($action){
if (!array_key_exists($action, $this->user_info['access_rights']))
return false;
return (bool) $this->user_info['access_rights'][$action];
}
}
class Guest_User extends User {
public function __construct() {
// Стандартные значения для гостя
$this->user_info['name'] = 'Uknown user';
$this->user_info['email'] = '';
$this->user_info['status'] = 'guest';
}
/**
* Гость может только читать
* @param string $action
* @return bool
*/
public function checkAccessRights($action) {
if ('read' == $action)
return true;
return false;
}
}
У нас есть два класса: User и Guest_User. Класс Guest_User является реализацией шаблона особый случай. Если нет возможности идентифицировать пользователя, то мы просто возвращаем экземпляр класса Guest_User, который имеет такой же интерфейс, как и класс User. Вызывающий код спокойно продолжает работать не замечая подмены объекта. Далее приведу пример использования данных классов:
<?php
// ...
function get_user_object(){
if (!isset($_SESSION['user']) || !($_SESSION['user'] instanceof User))
$_SESSION['user'] = new Guest_User();
return $_SESSION['user'];
}
Эта простая функция проверяет наличие объекта в сессии и если он не найден или не корректен, то создает нашу реализацию Special Case, иначе - значение из сессии.
На этом всё. Не забывайте подписываться на rss и оставлять свои комментарии!