|
 |
Путь: >
Общие вопросы
Общие вопросы
Автор: - KievMan
Дата публикации - 23.03.2006
Просмотров: - 3652
Обоснование событийно-ориентированного подхода программирования
[p]Введение[/p]
У многих программистов сложилась устойчивая ассоциация событий с паттерном Observer. Как только заходит речь о событиях, вспоминается эта конкретная модель, и все рассуждения проецируются на неё. Однако, события – это абстракция, которая существует независимо от языка программирования, от процедурного или объектного программирования и конкретной модели событий. В данной статье предпринята попытка, дать чёткое обоснование событийно-ориентированному подходу, определить его место в программировании. Все рассуждения будут проводиться на основе очень простого примера решения задачи.
[p]Решение первой задачи[/p]
Итак, представим себе задачу, по которой нам необходимо обработать данные, полученные из формы, представленной на рисунке.
[img=/img/ru/articles/form.png[ w=181 h=192]/img]
По условиям задачи необходимо, чтобы поле “Имя” содержало от 3 до 30 символов, не содержало цифр, и не было пустым. Те же условия распространяются и на поле “Фамилия”. Для каждой ошибки ввода должно быть соответствующее сообщение. Задача надуманная, но для целей данной статьи подойдёт.
Составим алгоритм выполнения указанной задачи:
[img=/img/ru/articles/activity.png[ w=615 h=945]/img]
и напишем по нему программу:
[php]
<?
$message = 'Данные приняты';
if ($_REQUEST['btSubmit']) {
if (empty($_REQUEST['edSurname'])) {
$message = 'Фамилия не должна быть пустой.';
} else {
if (strlen($_REQUEST['edSurname']) < 3 || strlen($_REQUEST['edSurname']) > 30) {
$message = 'Недопустимое количество знаков в фамилии.';
}
if (preg_match('|[\d]|', $_REQUEST['edSurname'])) {
$message = 'Фамилия не должна содержать цифр.';
}
}
if (empty($_REQUEST['edName'])) {
$message = 'Имя не должно быть пустым.';
} else {
if (strlen($_REQUEST['edName']) < 3 || strlen($_REQUEST['edName']) > 30) {
$message = 'Недопустимое количество знаков в имени.';
}
if (preg_match('|[\d]|', $_REQUEST['edName'])) {
$message = 'Имя не должно содержать цифр.';
}
}
print $message;
} ?>
[
/php]
Даже малоопытный программист скажет, что можно написать лучше, и предложит, например, вот такой вариант:
[php]
<?
function checkLength($field)
{
if (strlen($field) < 3 || strlen($field) > 30) {
return false;
}
return true;
}
function existsDigits($field)
{
if (preg_match('|[\d]|', $field)) {
return true;
}
return false;
}
$message = 'Данные приняты';
if ($_REQUEST['btSubmit']) {
if (empty($_REQUEST['edSurname'])) {
$message = 'Фамилия не должна быть пустой.';
} else {
if (!checkLength($_REQUEST['edSurname'])) {
$message = 'Недопустимое количество знаков в фамилии.';
}
if (existsDigits($_REQUEST['edSurname'])) {
$message = 'Фамилия не должна содержать цифр.';
}
}
if (empty($_REQUEST['edName'])) {
$message = 'Имя не должно быть пустым.';
} else {
if (!checkLength($_REQUEST['edName'])) {
$message = 'Недопустимое количество знаков в имени.';
}
if (existsDigits($_REQUEST['edName'])) {
$message = 'Имя не должно содержать цифр.';
}
}
print $message;
}?>
[
/php]
Однако давайте разберёмся, что же, по сути, было сделано, и чем второй вариант лучше первого.
Во втором варианте некоторая часть программы была оформлена в виде повторно используемого кода, а именно функции checkLength() и existsDigits(). Давайте подробно рассмотрим, как это было сделано.
Во-первых, были выделены похожие участки алгоритма. Такое выделение подразумевает, что была определена схожая последовательность действий, а также вход и выход этой последовательности. В результате мы получили две части алгоритма, основную часть и повторно используемую. На рисунке приведена часть алгоритма, реализованная в функции checkLength().
[img=/img/ru/articles/checklength.png[ w=342 h=355]/img]
Эта часть будет включена в основную часть алгоритма как атомарное действие. Она содержит в себе детали выполнения этого действия, поэтому её принято называть низкоуровневой частью алгоритма. Основная часть, соответственно, находиться на более высоком уровне, или уровне приложения.
Во-вторых, был определён интерфейс для взаимодействия двух частей алгоритма. Интерфейс – это абстракция, выделяющая взаимодействие между другими абстракциями, т.е. представляет собой характеристику взаимодействия абстракций, или протокол. В нашем случае интерфейсом является имя функции, передаваемые ей параметры и возвращаемое функцией значение. Глядя на этот интерфейс можно сказать, что основная часть алгоритма является клиентом по отношению к низкоуровневой части, и является инициатором взаимодействия. Низкоуровневая часть является серверной, по отношению к основной части алгоритма.
С этого момента интерфейс серверной части должен оставаться неизменным. Это необходимое условие повторно используемого кода. Благодаря интерфейсу появляется возможность вносить изменения в отдельные части программы, не нарушая работы остальных частей.
Все эти общие рассуждения были проделаны для того, чтобы решить другую задачу.
[p]Решение второй задачи[/p]
Представим себе задачу, в которой, дополнительно к требованиям первой задачи, добавилось ещё одно требование, чтобы буквы были только кириллического алфавита. Напишем программу с учётом нового требования:
[php]
<?
// Добавим новую функцию, остальные используем повторно, из первой задачи.
function checkLetters($field)
{
if (preg_match('|[а-яА-Я]|', $field)) {
return true;
}
return false;
}
$message = 'Данные приняты';
if ($_REQUEST['btSubmit']) {
if (empty($_REQUEST['edSurname'])) {
$message = 'Фамилия не должна быть пустой.';
} else {
if (!checkLength($_REQUEST['edSurname'])) {
$message = 'Недопустимое количество знаков в фамилии.';
}
if (!checkLetters($_REQUEST['edSurname'])) {
$message = 'Фамилия должна содержать буквы только кириллического алфавита.';
}
if (existsDigits($_REQUEST['edSurname'])) {
$message = 'Фамилия не должна содержать цифр.';
}
}
if (empty($_REQUEST['edName'])) {
$message = 'Имя не должно быть пустым.';
} else {
if (!checkLength($_REQUEST['edName'])) {
$message = 'Недопустимое количество знаков в имени.';
}
if (!checkLetters($_REQUEST['edName'])) {
$message = 'Имя должно содержать буквы только кириллического алфавита.';
}
if (existsDigits($_REQUEST['edName'])) {
$message = 'Имя не должно содержать цифр.';
}
}
print $message;
} ?>
[
/php]
Сразу видно, что у обеих программ есть много общего, а именно высокоуровневая часть алгоритма:
[php]
<?
if ($_REQUEST['btSubmit']) {
if (empty($_REQUEST['edSurname'])) {
[…]
} else {
[…]
}
if (empty($_REQUEST['edName'])) {
[…]
} else {
[…]
}
} ?>
[
/php]
Возникает желание вынести её в повторно используемый код. Чтобы сделать это проведём те же операции что и в первом случае.
Похожие участки алгоритма уже выделены, однако в этом случае нам нужно определить не вход и выход из алгоритма, а только точку выхода из него. Такие места обозначены на предыдущем листинге троеточием.
Далее, по аналогии, мы должны определить интерфейс взаимодействия. Так как в языке программирования нет средств описания интерфейса для точек выхода из алгоритма, мы вынуждены применять обходные средства. Например, мы можем поместить в точки выхода вызов заранее определённой функции:
[php]
<?
if ($_REQUEST['btSubmit']) {
if (empty($_REQUEST['edSurname'])) {
onEmpty($_REQUEST['edSurname']);
} else {
onInput($_REQUEST['edSurname']);
}
if (empty($_REQUEST['edName'])) {
onEmpty($_REQUEST['edName']);
} else {
onInput($_REQUEST['edName']);
}
} ?>
[
/php]
Теперь функции onEmpty() и onInput() выполняют роль интерфейса для взаимодействия с высокоуровневой частью алгоритма. Окончательный вид программы будет выглядеть так:
[php]
<?
function checkLetters($field)
{
if (preg_match('|[а-яА-Я]|', $field)) {
return true;
}
return false;
}
function onEmpty($field)
{
global $message;
$message = 'Поле не должно быть пустым.';
}
function onInput($field)
{
global $message;
if (!checkLength($field)) {
$message = 'Недопустимое количество знаков.';
}
if (!checkLetters($field)) {
$message = 'Допускаются буквы только кириллического алфавита.';
}
if (existsDigits($field)) {
$message = 'Цифры не допускаются.';
}
}
$message = 'Данные приняты';
if ($_REQUEST['btSubmit']) {
if (empty($_REQUEST['edSurname'])) {
onEmpty($_REQUEST['edSurname']);
} else {
onInput($_REQUEST['edSurname']);
}
if (empty($_REQUEST['edName'])) {
onEmpty($_REQUEST['edName']);
} else {
onInput($_REQUEST['edName']);
}
print $message;
}?>
[
/php]
В результате, мы можем использовать высокоуровневую часть алгоритма повторно. Теперь, если нам понадобиться решить подобную задачу, нам останется только установить взаимосвязи между высокоуровневой и набором низкоуровневых частей алгоритма, т.е. изменить функции onEmpty() и onInput().
[p]Выводы.[/p]
Как вы уже догадались, точки выхода из алгоритма, представленные функциями onEmpty() и onInput(), и есть события. Имя функций, принимаемые ими параметры и возвращаемые ими значения служат интерфейсом между двумя частями алгоритма, а тело функций и есть та точка выхода, в которой мы можем наращивать функциональность
Итак, события, это тоже интерфейс, который предназначен для оформления высокоуровневой части алгоритма в виде повторно используемого кода, т.н. выходной интерфейс. Вы могли встречаться с событиями под другими названиями. Например, функция PHP array_walk(), принимает имя так называемой callback функции, которая вызывается для каждого элемента массива. Примеров можно найти много, и суть у всех одна - отделить высокоуровневую часть алгоритма, оставив возможность изменять функциональность в определённой точке алгоритма.
Обратите внимание, что обоснование событий проводилось без ОО подхода, т.к. в действительности, это абстракция, которая не зависит ни от подхода программирования, ни от языка программирования. В статье приведена упрощённая реализация событий, задача которой упростить понимание сути событий.
Теперь, когда рассмотрена суть такой абстракции как события, появляется возможность рассмотрения различных реализаций этой абстракции. Один из примеров реализации можно посмотреть в статье "Событийно-ориентированные приложения в PHP" [1]
[p]Ссылки[/p]
[li][1] Статья [url=http://php.com.ua/ru/articles/other/php_events.htm]Событийно-ориентированные приложения в PHP[/url][/li]
[li][2] Проект [url=http://www.anter.com.ua/PApple/]PHP_Application[/url] – событийно-ориентированный фрэймворк.[/li]
[li] [3] Статья [url=http://php.com.ua/ru/articles/other/papple-getting-started.htm]PHP_Application – первое знакомство[/url] [/li]
Обсудить в ФОРУМе - комментариев (0)
Путь: >
Общие вопросы
Если вы заметили орфографическую, стилистическую или другую ошибку на этой странице, просто выделите ошибку мышью и нажмите Ctrl+Enter.
|
|