Что такое паттерн проектирования

 

Паттерн проектирования это шаблон, структура или фреймворк, на которых вы можете создать свой код приложения. Когда термин фреймворк используется в связи с PHP, большинство людей сразу же думают о MVC, в действительности, MVC является лишь одним из многих шаблонов проектирования, которые будут обсуждаться здесь.

 

Singleton

Паттерн singleton  часто используется  во многих приложениях когда требуется только один экземпляр ресурса. Наиболее очевидный пример такого ресурса для PHP это соединения с базой данных, хотя паттерн может быть использован и на другие типы ресурсов. Для создания динамической страницы нужно несколько обращений к базе данных. Если использовать лишь один экземпляр ресурса доступа к бд, а не создавать каждый раз новое соединение, накладные расходы будут сведены к минимуму. Один экземпляр в этом случае, создается паттерном Singleton.

 

class db{
/*** Объявим статичное свойство $instance в котором будем хранить указатель 
на наш единственный экземпляр***/
private static $instance = NULL;

/**
 * constructor класса делаем приватным чтобы 
 * никто не смог создать новый экземпляр используя оператор new
 */
private function __construct() {
  /*** может быть добавим сюда какой-то код позже ***/
}

/**
 * Возвращает $instance если соединение было создано ранее в 
 * противном случае инициализирует новое соединение
 *
 * @return object (PDO)
 * @access public
 */
public static function getInstance() {
 if ( !self::$instance ){
    self::$instance = new PDO("mysql:host='localhost';dbname='animals'", 'username', 'password');
    self::$instance->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
 }
 return self::$instance;
}

/**
 * Так же как и constructor, делаем __clone приватным методом
 * чтобы никто не мог клонировать данное соединение
 */
private function __clone()

} /*** конец класса ***/

 

Посмотрим на то, что произошло в singleton классом выше. Объявлена статичная приватная переменная $instance, это гарантирует, что  никто не получить доступ к ней напрямую, а также то что всегда буде существовать лишь 1 экземпляр этой переменной self::$instance. Кроме того, __constructor и __clone методы были сделаны приватными, чтобы предотвратить клонирование класса, или создание еще одного его экземпляра. Класс имеет один метод для получения ресурса соединения, getInstance(). getInstance проверяет существует ли экземпляр. Если экземпляр не существует, то создается новый  и присваивается self::$instance. Если экземпляр класса был создан ранее, getInstance () вернет указатель на него. Результатом является то, что возвращаемое значение всегда один и тот же экземпляр, и новый ресурс, и расходы на его создание не требуются. Вот небольшая демонстрация того, как это может быть использовано в приложении.

 

try{
 /*** запрос в базу ***/
 $result = DB::getInstance()->query("SELECT animal_type, animal_name FROM animals");
 /*** цикл изъятия результата работы ***/
 foreach($result as $row){
  echo $row['animal_type'] .' - '. $row['animal_name'] . '< br />';
 }
}catch(PDOException $e){
    echo $e->getMessage();
}

 

Фабрика

Паттерн Factory(Фабрика), создает объекты для вас, без необходимости использовать ключевое слово new, чтобы создать его самим. Factory, как следует из названия, это фабрика для создания объектов. Зачем нам это надо? Давайте рассмотрим приложение, которое использует INI-файл конфигурации. Затем приложение изменено, чтобы получить настройки из базы данных. В результате приложения разваливается, как карточный домик, как только базы удаляется. Это тесная связь объектов, где каждый объект в значительной мере опирается на другой, создает проблемы в больших приложений. В нашем примере файлом конфигурации становятся база данных, если другие классы находятся в зависимости от класса, который читал из файла INI, и неожиданно классу было поручено  чтение данных из базы, то неизбежны проблемы. Используя модель паттерн проектирования factory , если вы измените тип объекта класса чтения INI в на класс базы данных вам нужно только изменить фабрику. Любой другой код, который использует фабрику будет обновляться автоматически.

 

/**
 * @interface config
 */
interface Config{
  function getName();
}

/**
 *
 * @class config
 *
 */
class userConfig implements Config{
 public $user_id;
 /*** конструктор ***/
 public function __construct( $id ){
    $this->user_id = $id;
 }
 /*** возвращает экземпляр собственного класса ***/
 public static function Load( $id ){
    return new userConfig( $id );
 }

 public function getName(){
    try{
        /*** sql запрос ***/
        $sql = "SELECT username FROM user_table WHERE user_id=:user_id";
        $db = db::getInstance();
        $stmt = $db->prepare($sql)
        $stmt->bindParam(':user_id', $this->user_id, PDO::PARAM_INT);
        return $stmt->fetch(PDO::FETCH_COLUMN);    
    }catch(PDOException $e){
        /*** тут обрабатываем handle исключения ***/
        return false;
    }
 }

} /*** конец класса ***/

/*** возвращает экземпляр config ***/
$conf = userConfig::Load( 1 );
echo $conf->getName();

 

Может показаться что это немного чрезмерной код для извлечения просто имени пользователя. Но в приложений большого масштаба, где переход от системы на файлах к  системе на базе данных, результаты могут быть катастрофическими. Простое изменение метода getName в классе userConfig на работу с другими типами данных(INI) никак не затронет остальные классы.

 

MVC - Модель Вид Контроллер 

MVC это еще один весьма часто встречающийся шаблон проектирования в PHP. Множество людей используют фреймворки исполненными в виде MVC или его вариациях. Эта тема настолько обширна что я уделил ей отдельный топик на xdan.ru, тем не менее тема будет вкратце и здесь.

MVC базируется на принципе разделения бизнес логики (Модель) и логики отображения (Вид). Идея, что изменения в отображении, не должны вмешиваться в модель . Так же как изменения в Модели, или изменение данных, не должно влиять на то как данные будут отображены (Вид).

Введя третью сторону паттерна (Контроллер), модель и представление могут быть независимы друг от друга. Контроллер обрабатывает данные, вводимые пользователем через интерфейс (Вид) и передает эту информацию в Модель. Если модель должна ответить, контроллер принимает этот ответ и поставляет в Вид. Это разделение бизнес-логики и логики представления дает разработчикам приложений более широкую гибкость и способствует повторному использованию кода.

 

Observer

Паттерн проектирования Observer(Наблюдатель) так же, как и паттерн фабрики, обеспечивает метод ослабления тесной связи между объектом. Паттерн состоит из двух объектов. Один объект просто работает как работал(наблюдаемый объект), а другой объект наблюдает за ним. Посмотрим как это работает

 

// наблюдатель
interface myObserver{
    function onChanged( $sender, $args );
}

// интерфейс к наблюдаемому объекту
interface myObservable{
    function addObserver( $observer );
}

class CartItem implements myObservable{
 /**
  * @observer array
  */
 private $cart_observers = array();

 public function addItem( $name ){
    foreach( $this->cart_observers as $obs ){
        $obs->onChanged( $this, $name );
    }
 }

 /*** добавляем объект в наблюдаемые объекты ***/
 public function addObserver( $observer ){
    $this->cart_observers []= $observer;
 }

} /*** конец класса ***/

// реализация наблюдателя
class CartItemLogger implements myObserver {
 public function onChanged( $sender, $args ){
 /*** просто добавляем в лог что объект был изменен***/
    echo( "Logging: $args добавлен в карзину" );
 }

} /*** конец класса ***/

// создаем наблюдаемый объект
$cart = new CartItem();
/*** добавим наблюдатель, который будет срабатывать на изменение $cart ***/
$cart->addObserver( new CartItemLogger() );
$cart->addItem( "book" );//изменяем $cart

 

Сила паттерна Observe в том что объекту $cart не важно, что будет делать обработчик его изменения. К этому объекту могут быть привязаны и другие наблюдатели отвечающие за другие события. Другими словами созданы обработчики события, а весь паттерн это ступень в событийное программирование.

Оригинал статьи

Оставлять комментарии могут только зарегистрированные пользователи

Комментарии  

Дмитрий
# Дмитрий 21.01.2012 15:55
Вы случаем не забыли сделать вывод ошибок? если я не ослеп то да!
Лерик
# Лерик 21.01.2012 17:38
Ммм?
апыв
# апыв 11.12.2012 22:56
Это уже почти синглтон.) В кучу получилось.



$conf = userConfig::Load( 1 );
Leroy
# Leroy 13.12.2012 20:07
что вы имеете ввиду?
Алексей Алексеевич
# Алексей Алексеевич 28.05.2016 12:46
Спасибо!
Очень познавательные паттерны. Про Singleton для работы с БД, я так понял, нет необходимости закрывать соединение, т.к. оно закроется после завершения скрипта? Просто в некоторых классах я замечал, когда они созданы через new, везде в деструкторе был использован метод mysqli->close(). Ваш пример класса мне показался более лаконичным и интересным.
Leroy
# Leroy 30.05.2016 10:33
Ничего не мешает в __destruct закрывать соединение. Насколько помню это не обязательно, потому что php скрипт в любом случае умирает и закрывать ничего не нужно. Это было бы нужно если у вас сотни разных подключений в одном скрипте. Тогда нужно следить за открытием/закрытием. Деструктором/конструктором.