[ Поиск ] - [ Пользователи ] - [ Календарь ]
Полная Версия: Курс "MVC от теории к практике"
Alehandr
Observed и Observer

Всем привет! На этом занятии мы реализуем паттерн Observer и немного допишем основной код.

Подготовка к реализации:
Наш расширенный паттерн будет использовать исключения, но не стандартные, а свои, для этого и пропишем файл core/exceptions.php:
<?php if (!defined('APP')) die('Access is forbidden!!!');

class InterruptionException extends Exception { }

Ну и подключим их в индексе:
<?php

define('DS', DIRECTORY_SEPARATOR); //Только для сокращения
define('EXT', '.php'); //Расширение php файлов

define('APP', realpath(dirname(__FILE__)) . DS . 'app' . DS); //Корневая директория приложения
define('CORE', realpath(dirname(__FILE__)) . DS . 'core' . DS); //Корневая директория системных классов
define('MODS', realpath(dirname(__FILE__)) . DS . 'modules' . DS); //Корневая директория модулей

require(CORE . 'exceptions' . EXT); //Импортируем исключения
require(CORE . 'functions' . EXT); //Импортируем функции ядра (__autoload)

Starter::Run(); //Запускаем приложение...


Observer (core/classes/observer.php):
<?php if (!defined('APP')) die('Access is forbidden!!!');

class Observer_Core {

/**
*
@var mixed Наблюдатели
*/

public $observers = array();

/**
* Singleton
*
*
@return Observer
*/

public static function &instance() {
static $instance = null;
if ($instance === null) {
$instance = new Observer();
}
return $instance;
}

/**
* Прикрепление наблюдателя
*
*
@param string $class
*
@param string $event
*
@param mixed $callback
*/

public function Bind($class, $event, $callback) {
$class = strtolower($class);
$event = strtolower($event);
if (!isset($this->observers[$class . '|' . $event])) {
$this->observers[$class . '|' . $event] = array();
}
$this->observers[$class . '|' . $event][] = $callback;
}

/**
* Выполнение действия, возращает прерывание
*
*
@param string $class
*
@param string $event
*
@param mixed $args
*
@return bool
*/

public function FireEvent($class, $event, &$args) {
$class = strtolower($class);
$event = strtolower($event);
$interruption = false;
if (isset($this->observers[$class . '|' . $event])) {
foreach ($this->observers[$class . '|' . $event] as $callback) {
try {
call_user_func($callback, &$args);
} catch (InterruptionException $ex) {
$interruption = true;
}
}
}

return $interruption;
}

}


if (!defined('OBSERVER_CLASS')) {
class Observer extends Observer_Core { }
}

Что позволяет делает этот класс? Это вы увидите в примере использования...

Observed (core/classes/observed.php):
<?php if (!defined('APP')) die('Access is forbidden!!!');

abstract class Observed_Core {

public function FireEvent($event, &$args = array()) {
return Observer::instance()->FireEvent(get_class($this), $event, &$args);
}

}


if (!defined('OBSERVED_CLASS')) {
abstract class Observed extends Observed_Core { }
}

Для чего эта морока? Для того, чтобы облегчить использование предыдущего класса... и все...

Тестирование:
Для тестирования напишем классы Starter и Router:

Starter (core/classes/starter.php):
<?php if (!defined('APP')) die('Access is forbidden!!!');

class Starter {

/**
* Запуск приложения
*/

public static function Run() {
self::LoadModules();
$args = array('route' => self::GetRoute());
if (!Router::instance()->FireEvent('run', $args)) {
Router::instance()->Run($args['route']);
}
}


/**
* Загрузка модулей
*/

public static function LoadModules() {

}


/**
* Определение маршрута для Маршрутизатора (Router'а)
*
@return string
*/

public static function GetRoute() {
$route = isset($_GET['route']) ? trim($_GET['route'], '/') : '';
if ($route == 'index.php') {
$route = '';
}
return $route;
}

}


Router (core/classes/router.php):
<?php if (!defined('APP')) die('Access is forbidden!!!');

class Router_Core extends Observed {

/**
* Singleton
*
*
@return Router
*/

public static function &instance() {
static $instance = null;
if ($instance === null) {
$instance = new Router();
}
return $instance;
}

public function Run($route) {
echo 'Router run: ' . $route;
}

}


if (!defined('ROUTER_CLASS')) {
class Router extends Router_Core {}
}


Перейдите по адресу http://core/hello/world.php (http://core/ - локальный сайт с наработками), вы увидите вывод hello/world.php - это маршрут...
Ну и самое интересное, изменим немного код индекса (потом верните как было):
<?php

define('DS', DIRECTORY_SEPARATOR); //Только для сокращения
define('EXT', '.php'); //Расширение php файлов

define('APP', realpath(dirname(__FILE__)) . DS . 'app' . DS); //Корневая директория приложения
define('CORE', realpath(dirname(__FILE__)) . DS . 'core' . DS); //Корневая директория системных классов
define('MODS', realpath(dirname(__FILE__)) . DS . 'modules' . DS); //Корневая директория модулей

require(CORE . 'exceptions' . EXT); //Импортируем исключения
require(CORE . 'functions' . EXT); //Импортируем функции ядра (__autoload)

class ObserverTester {
public function route(&$args) {
$args['route'] = preg_replace('#\.php$#ui', '', $args['route']);
//throw new InterruptionException(); //Если раскомментировать, маршрутизатор не запустится
}
}

Observer::instance()->Bind('router', 'run', array('observertester', 'route'));

Starter::Run(); //Запускаем приложение...


Обновите открытую страничку, вывод изменится на hello/world.

В итоге:
Теперь мы можем использовать данный паттерн для изменения некоторых параметров вызова, без изменения логики основных классов, а также прерывать и замещать некоторые действия (в конце всех лекций приведу пример использования данной фишки для написания модуля Ajax запросов).

Задание:
Эксперементируйте с кодом...



Спустя 1 час, 53 минуты, 48 секунд (24.11.2009 - 14:04) Argnist написал(а):
не понял строк
public static function &instance()
{
static $instance = null;
if ($instance === null)

что такое &instance() и зачем проверка, если итак null?

Спустя 9 минут, 48 секунд (24.11.2009 - 14:14) glock18 написал(а):
&instance значит, что функция возвращает ссылку
static $instance - статическая переменная, она сохраняет значение после выполения функции.
так при следующем вызове, она уже будет не null.

Спустя 1 час, 34 минуты, 31 секунда (24.11.2009 - 15:49) Greg1978 написал(а):
У меня такой вопрос.
Для чего нужен промежуточный "пустой" абстрактный класс Observed между абстрактным Observed_Core и дочерним Router.
Впринципе , мне кажется, хватило бы для описания сущности и одного абстрактного класса.
Возможно было бы и отлично задать интерфейс для "наблюдателей"

interface Observed_Core {
public function FireEvent($event, &$args = array());
}

Теперь при прикреплении наблюдателей в Observer можно задать проверку типов обьектов, которые принадлежат к интерфейсу Observed_Core и соответсвенно имееют метод FireEvent.
Спасибо!

Спустя 14 часов, 26 минут, 31 секунда (25.11.2009 - 06:15) Alehandr написал(а):
Greg1978
Observed - это просто прослойка, чтобы кода меньше писать и без ошибок.

Observed_Core и в том-же файле пустой Observed, наследующий Observed_Core позволяют расширять стандартные возможности. К примеру: если создать файл app/classes/observed.php и в нем прописать class Observed extends Observed_Core { } только с расширенным функционалом - использоваться будет он.

Спустя 4 часа, 17 минут, 57 секунд (25.11.2009 - 10:33) Greg1978 написал(а):
В нашем случае два абстрактных класса подряд.

Спустя 1 год, 4 месяца, 14 дней, 7 часов, 1 минута, 29 секунд (9.04.2011 - 16:35) 777dss написал(а):
Объясните пожалуйста, а зачем в FireEvent() передавать &$args по ссылке ?...
И если всё же это необходимо, то почему тогда идёт вызов функции call_user_func(), а не функции call_user_func_array() - просто вроде в первую из них нельзя передавать по ссылке...
Быстрый ответ:

 Графические смайлики |  Показывать подпись
Здесь расположена полная версия этой страницы.
Invision Power Board © 2001-2024 Invision Power Services, Inc.