Перейти к контенту

Блог команды AlterVega

  • запись
    121
  • комментария
    4
  • просмотра
    106 552

AlterVega

1 064 просмотра

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

 

Проблемы модификации в современных платформах

 

Будучи активными разработчиками сообществ на основе готовых решений (vBulletin, IP.Board, phpBB) мы постоянно сталкивались с проблемами в разработке, но, пожалуй, самая досаждающая из них - отсутствие возможности безболезненно для ядра системы вносить изменения в ее базовое поведение. Решение данной проблемы, как правило, выливается в необходимость внесения изменений в файлы самой системы, что влечет за собой проблемы при обновлениях. Если такие изменения глобальны, то актуализация скриптов ядра системы становится фактически непосильной задачей (прощайте исправления ошибок, оптимизации и новые возможности).

 

http://altervega.ru/forums/upload/forum/2012/03-19/a42fb8aa9cffc5937fe0c2a5de5f7d85.jpeg

 

Есть проблемы и поменьше, вроде отсутствия удобного внутреннего API, запутанности логики приложения и отсутствия документации. Но они не так сильно влияют на разработку, как выше описанная. Конечно, перечисленные решения представляют механизмы расширения, однако, они являются несколько инородными, так как изначально не закладывались в архитектуру системы, а добавлялись уже в готовую.

 

Итак перед нами стояла задача сделать AlterVega гибкой и расширяемой настолько, чтобы разработчик мог ее улучшить без последствий для самой системы.

 

Первые шаги в решении этой задачи были предприняты еще на этапе проектирования архитектуры системы. В качестве основы мы выбрали идею реализации веб-приложения на основе паттерна Model-View-Controller (MVC). А при дизайне классов системы мы придерживались SOLID принципов и необходимых уровней их связанности (Low Coupling and High Cohesion).

 

После того как у нас появился работающий прототип системы, мы попробовали нарастить его возможности, при этом не нарушив концепции, заложенной в архитектуру приложения. В результате чего у нас появился Dependency Container.

 

Dependency Container (DC)

 

Контейнер предоставляет внешние зависимости программному компоненту системы. Контейнер гибко настраивается, что позволяет разработчикам управлять зависимостями в ходе расширения системы.

 

Например, при построении списка тем у нас выводится их рейтинг. Работа с рейтингом вынесена в отдельный программный компонент, потому для компонента вывода списка тем он является внешней зависимостью.

 

/* Получение рейтинга темы и проверка голосования за неё */
$rating_service = $this->_services->get( 'forum.thread.rating' );
$rating_service->fill( array( $thread ) );

 

Здесь сервис рейтинга получается из контейнера. В настройках контейнера есть запись:

 

<service id="forum.thread.rating.strategy" shared="false"/>
<service id="forum.thread.rating">
<argument type="service" id="forum.thread.rating.strategy"/>
</service>

 

Разработчик может просто поменять описание сервиса рейтинга, например, добавив кеширование:

 

<service id="forum.thread.rating.strategy" shared="false"/>
<service id="forum.thread.rating.base">
<argument type="service" id="forum.thread.rating.strategy"/>
</service>
<service id="forum.thread.rating">
<argument type="service" id="core.cache" />
<argument type="service" id="forum.thread.rating.base"/>
</service>

 

При условии, что класс MyPlugin_Service_Cached_Rating будет реализовывать необходимый для сервиса рейтинга интерфейс, система начнет работать уже с кешируемым рейтингом.

 

Как видно, настройка контейнера позволяет изменять базовые классы системы, что дает возможность без изменения базового кода, менять базовое поведения системы.

 

Следующим шагом стало добавление инструментария для расширения возможностей системы. Если на шаге с внедрением контейнера мы преследовали возможность изменить существующее поведение, то на этом - добавить новое. Здесь уже нужна система не подмены существующих классов и объектов, а их расширение.

 

Система расширения классов

 

Мы отталкивались от того, что расширений даже для одной конкретной возможности может быть несколько, а авторы могут не знать друг о друге. Замена имени класса в настройках DC обеспечивает возможность существования только одного расширения. Это нам не подходит.

 

Мы усовершенстовали наш контейнер, добавив возможность расширять базовый класс, классами расширений. Выглядит это так:

 

<service id="user">
<argument type="service" id="user.mapper"/>
<extended by="Author1_MyPlugin_Service_User"/>
<extended by="Author2_MyPlugin_Service_User"/>
<extended by="Author3_MyPlugin_Service_User"/>
</service>

 

В итоге базовый класс сервиса User_Service_User будет обладать наряду со своими обычными возможностями и всеми возможностями из классов Author1_MyPlugin_Service_User, Author2_MyPlugin_Service_User и Author3_MyPlugin_Service_User.

 

Имеющиеся методы модифицирования позволяют изменять и добавлять новые возможности. Однако такие изменения работают на основе полиморфизма, а значит, если нам потребуется поменять поведение метода класса, например, в середине, то нам потребуется скопировать части базового кода. В результате появляется зависимость модификации от базового кода, что при изменении этого кода приведет к поломке или неверной работе модификации. Для решения данной проблемы мы сделали еще один шаг в развитии нашей системы модифицирования - добавили событийную систему в базовые классы приложения.

 

Система событий

 

Идея заключается в том, что при изменении состояния одного из объектов все зависящие от него оповещаются об этом событии. Соответственно зависящие объекты при наступлении определенных событий могут выполнять какие-то действия. Например, при добавлении нового комментария в тему, в систему событий поступает уведомление об этом. На событие добавления комментария подписаны модуль рассылки уведомлений и модуль пересчета рейтинга, которые рассылают уведомления подписавшимся на тему, и пересчитывают рейтинг авторам темы и комментария, соответственно.

 

В коде это выглядит так:

Порождение сигнала о создании нового входящего сообщения в личной почте

 

$signals->emit( 'user.before.message.inbox.created', $message );

 

Один из подписчиков на данное событие, уведомляющий пользователя о создании личного сообщения и заносящий это событие в ленту обновлений

 

/**
* Класс слушателя событий личных сообщений
*/
class User_Class_Signal_Listener_Message extends jE_Signal_Listener_Abstract
{
/**
* Запись события в ленту обновлений
*
* @param jE_Signal $signal Данные для обработки сигнала или объект сигнала
* @return jE_Signal
*/
public function afterCreate( jE_Signal $signal )
{
   	$message = $signal->getData();
   	$dc = jE::dc();
   	$dc->get( 'user.message.log' )->logEvent( $message );
   	$dc->get( 'user.message.notify' )->notify( $message );
   	return $signal;
}
}

 

Подписчики на события добавляются разработчиками через конфигурационный файл /_protected/resources/configs/listeners.php. Указывается событие, класс обработчика и метод класса для обработки события

 

'user.after.message.inbox.created' =>
 array (
'User_Class_Signal_Listener_Message' => 'afterCreate',
 ),

 

Порождение сигналов сделано в основных местах создания, изменения, удаления сущностей. К сожалению, внесение инструкций для порождения сигналов требует измений в базовом коде, поэтому мы открыты для добалений таких точек в базовый код.

 

Как вы видите, AlterVega обладает рядом существенных способов изменения и расширения базового поведения системы без редактирования кода базовых классов. До недавнего времени они оставались всего лишь способами, но мы сделали описательный формат, позволяющий собирать изменения в модификации, и внедрили механизм подключения модификаций из директории ресурсов.

 

Поставка модификаций

 

Файлы классов модификации должны храниться в папке /_protected/resources/plugins/. Каждая модификация хранится в отдельной директории с именем модификации. Модификация поставляется с конфигураций config.xml, расположенный в директории модификации.

Пример файла конфигурации:

 

<?xml version="1.0" encoding="UTF-8"?>
<plugin>
<name>Название модификации</name>
<author>Автор модификации</author>
<config>
   	<signals>
       	Список обработчиков системных сигналов
   	</signals>
   	<services>
       	Список новых объектов контейнера служб
   	</services>
   	<extensions>
       	Список расширяемых объектов контейнера служб
   	</extensions>
   	<includes>
       	Список модификаций шаблонов
   	</includes>
   	<tasks>
       	Список новых задач
   	</tasks>
   	<settings>
       	Список новых настроек
   	</settings>
</config>
</plugin>

 

Для дистрибуции модификации достаточно запаковать директорию с классами и ресурсами модификации. Администраторы сообществ через администраторский центр смогут импортировать данный архив с модификацией и он автоматически будет развернут и подключен к системе. Администратор также сможет отключать или полностью удалять установленные модификации. Прототип интерфейса можно увидеть на скриншоте

 

http://altervega.ru/forums/upload/forum/2012/03-19/ebe0e2cdb73909cc385093475f748a03.png

 

Планы

 

Система модификаций уже внедрена в основную ветку разработки и будет выпущена в версии 1.1. С помощью модификаций вы сможете изменить большую часть классов компонентов системы, в дальнейших планах у нас доведение этого показателя до 100%. И, конечно же, мы хотим выслушать все вопросы, предложения и пожелания по данной системе, а так же точкам обработчиков системных событий.

0 комментариев


Рекомендуемые комментарии

Комментариев для отображения не найдено.

×
×
  • Создать...

Важная информация

Находясь на нашем сайте, вы соглашаетесь на использование файлов cookie, а также с нашим положением о конфиденциальности Политика конфиденциальности и пользовательским соглашением Условия использования.