Выбираем лучший инструмент: ООП в PHP
Автор: Vladimir Shapiro
Источник: detail.phpclub.net
Предполагаемая
аудитория
Эта статья рассчитана на начинающих PHP-программистов,
которые желают получить представление об
объектно-ориентированном и процедурном подходах написания
программ.
Предполагается, что читатели владеют основными знаниями в
области PHP и классов.
Вступление
"Настоящий гений проявляет себя в умении обрабатывать
неточную и противоречивую информацию" (Уинстон Черчиль)
В отличие от большинства других языков, PHP предоставляет
возможность использовать при программировании как
объектно-ориентированный, так и процедурный подходы.
Большинство PHP-программистов по умолчанию используют
последний, поскольку сами веб-страницы обрабатываются именно в
процедурном подходе (одни тег, затем другой, затем следующий и
т.д.). Код, написанный в процедурном подходе, гораздо проще
комбинируется с HTML и, как следствие, программисты зачастую
вырабатывают свой собственный стиль, основанный на таком
подходе.
Если вы новичок в PHP, то, скорее всего, единственный
способ программирования, которым вы пока пользовались - это
процедурное. Тем не менее, если вы уже исследовали ресурсы,
посвященные PHP, в поисках новых решений и приемов, вы
наверняка сталкивались с сообщениями на тему "распухших
объектов" (object bloat), или находили различные руководства
по написанию объектно-ориентированного кода на PHP. Возможно,
вы скачивали библиотеки, которые использовали классы, учились
создавать объекты и вызывать их методы. При этом, вполне
возможно, что вы никогда до конца не осознавали почему в том
или ином случае имеет смысл применять объектно-ориентированный
подход.
У обоих подходов есть свои преимущества и недостатки,
заставляющие их приверженцев обмениваться незамысловатыми
репликами вида "Объекты - это плохо!" или "Объекты - это
хорошо!". Данная статья не является попыткой склонить читателя
в сторону того или иного подхода, а призваны исследовать
преимущества и недостатки каждого.
Вот пример процедурного кода, выводящего строку
<?php
print "Hello, world.";
?>
Вот пример объектно-ориентированного кода, который делает
то же самое:
<?php
class helloWorld {
function myPrint() {
print "Hello,
world.";
}
}
$myHelloWorld = new helloWorld();
$myHelloWorld->myPrint();
?>
Сразу отметим, что эта статья не является попыткой дать
определение объектно-ориентированному программированию как
программированию с использованием классов. Для получение
начальных сведений о принципах ООП рекомендуется
воспользоваться Googlе. На данный момент существует
достаточное количество хороших (и немного плохих) статей,
посвященных этой теме.
Кто пишет подобный
код?
Для того, чтобы понять каким образом рассматриваемая тема
стала причиной многочисленных "религиозных войн" давайте
изучим пару экстремальных примеров с обоих сторон. Вначале мы
взглянем на "Процедурного Фанатика", а затем на "Объектного
Фанатика". Возможно кто-то из этих двоих покажется знакомым и
самому читателю.
"Процедурный
Фанатик"
"Процедурный Фанатик" всегда подвергается критики со
стороны своего учителя по информатики за плохое использование
абстрактного подхода при выполнении своих заданий. Приводимый
в оправдание аргумент: "Но ведь оно работает!" так и не
улучшает его отметок. В дальнейшем, такой программист избирает
в качестве предметной области такие задачи как написание
драйверов, файловых систем и других низкоуровневых программ,
где его акцент на скорости и лаконичности приносит ему
признание.
Экстремальными примерами "Процедурного Фанатика" являются
полное отрицание объектов и критика абстракции как таковой.
Такие программисты всегда хотят сделать свой код быстрым, не
заботясь особенно о тех, кто будет его читать позднее. Очень
часто они рассматривают процесс программирования как
соревнование, а не командную разработку. Они любят участвовать
в заковыристых конкурсах по написанию кода. Их любимыми
языками наравне с PHP являются C и Assembler. В PHP они могут
заниматься написанием модулей PECL, способствуя тому, чтобы
код был рациональным и эффективным.
"Объектный фанатик"
Объектный фанатик совсем недавно открыл для себя ООП и
теперь готов применять его везде! Он не до конца осознает
проблему производительности при используемом им подходе.
Иногда кажется, что концепции абстрактного дизайна для него
гораздо важнее самого кода. Возможно такой програмист имеет
хорошие шансы на карьеру менеджера проектов или технического
писателя.
Объектные фанатики очень любят упрекать процедурных в том,
что без абстракций мир до сих пор программировал бы с помощью
нулей и единиц. Они известны так же тем, что говорят на
псевдокоде. Экстремальным примером таких программистов
являются люди, использующие объекты, забывая о
производительности и не щадящие элегантности и даже читаемости
собственного кода. Их любимыми языками программирования помимо
PHP являются Java и Smaltalk. В PHP сообществе они как правило
занимаются разработками модулей PEAR, создавая хорошо
документированный и легко сопровождаемый код.
"Эти двое никогда не
встретятся"
Почему страницы форумов забиты предвзятыми
высказываниями? Дело в том, что уникальный опыт, накапливаемый
вами в процессе работы над проектом и, как следствие,
выработанная им "ваша философия" предопределяют путь поиска
новых идей. Как программисты, мы должны постоянно остерегаться
подобных предубеждений, оставаясь открытыми для изучения новых
вещей. Как информатики (computer scientists) мы должны уметь
отбрасывать эти предубеждения, чтобы находить лучшие методы
решения проблемы в конкретной ситуации.
Каковы ваши
цели?
Задумайтесь на мгновенье над тем, какие цели стоят
перед вами при написании той или иной PHP программы. Часто эти
цели выражаются очень туманно, иногда указываются в задании.
Вы можете придерживаться этих целей в любом из своих проектов.
Одна из них, за которую особо ратует автор этой статьи -
элегантность. Определение элегантности не будет дано здесь,
так как эта тема скорее подходит для отдельной статьи. Есть и
другие более абстрактные цели, которые не всегда сопоставимы с
теми решениями, которые хороши для проекта - для конкретных
задач, стоящих перед вами. Фактически, такие цели могут быть в
данном случае теми самыми предубеждениями, о которых
говорилось ранее.
Абстрактные цели
- Предложить решение, содержащее как можно меньше строчек
кода
- Думать о проблеме на уровне проблемы
Звучит великолепно, не правда ли? Но что понимается под
выражением "как можно меньше строчек кода"? Учитываются ли
комментарии? Должны ли мы соединять весь код в одну строчку,
разбивая ее точками с запятой? А как насчет фигурных скобок?
Некоторые любят оставлять для них отдельную строку. Судя по
поставленной цели, все это будет прямыми нарушениями задания.
Как насчет "решения на уровне проблемы"? Означает ли это, что
для каждой концепции, используемой в решении, мы должны
создавать класс? Или нам необходимо размещать каждую подзадачу
в отдельный файл и создавать сложное дерево, отображающее
реальную структуру? Вот оно - файл и класс на каждую мысль!
Очевидно, что такие обобщения, возведенные в принцип,
становятся нелепыми. Но есть и более тонкие проявления той же
самой проблемы. Как часто программисты, работающие в команде
изобретают решение в одну строчку и вставляют его в код без
всякого комментария, только лишь для того, чтобы поставить в
тупик своих товарищей по команде, которые будут затем этот код
сопровождать? И наоборот, как часто погрязший в бюрократизме
главный программист впадает в транс, создавая интерфейсы и
классы, которые не только осложняют жизнь его подопечным,
реализующим код, но и ограничивают производительность и
гибкость самого программы в случае, когда конечному заказчику
потребуется дополнительная функциональность. Все это всего
лишь незаметные проявления тех же самых предубеждений, которые
подсознательно работают в головах наших "слишком человечных"
программистов.
Практические цели
Решением проблемы является такой подход, при котором в
первую очередь рассматриваются практические цели, стоящие
перед проектом. Каковы же цели конкретного проекта? Вот
несколько вариантов:
- Писать это быстро, выпускать (release) часто.
- Заставить это работать настолько быстрее, насколько
возможно.
- Сделать это простым в сопровождении, распространении и
расширении
- Опубликовать программный интерфейс (API)
Первые две цели больше тяготеют к процедурному подходу,
тогда как последние две - к объектно-ориентированному.
Какой подход наиболее
правильный?
Теперь давайте попробуем оценить практические
преимущества каждого метода.
Процедурный подход
Основным аргументом в пользу процедурного подхода является
тот факт, что PHP является интерпретатором. Это означает, что
в отличие от многих других языков он не компилируется в
бинарный код для исполнения, а интерпретируется и исполняется
"на лету". Это язык сценариев и каждый сценарий хранится как
обычный текст (естественно, за исключением инструментов
компилирования Zend).
Еще одним аргументов против использования
объектно-ориентированного подхода в PHP4 является тот факт,
что подобная функциональность не была полностью продумана в
этой и более ранних версиях. Если послушать Расмуса (Расмус
Лердорф - автор первых версий PHP), то получается, что
поддержка ООП была добавлена уже после окончания основных
работ над ядром языка. Из этого следует, что использование
объектов в PHP4 не настолько рационально и эффективно, как
могло бы быть. Как бы то ни было, эта ситуация должна
измениться с выходом новой версии PHP5.
С использованием процедурного подхода написаны такие
популярные приложения как osCommerce и phpMyAdmin. Они
появились достаточно быстро и обладают относительно высокой
скоростью работы. Оба проекта сильно привязаны к HTML, который
без ограничений комбинируется с PHP.
osCommerce
osCommerce содержит известное количество объектов, но
большинство работы осуществляется в процедурном стиле. Автору
статьи приходилось не раз править исходный код, добавляя
нужную клиентам функциональность (порой настолько нужную, что
удивителен сам факт того, что она не была предусмотрена в
самом начале). Как бы то ни было, необходимо отметить, что
процесс добавления изменений не был тривиален. Большинство
процедурного кода требовало изрядное количество времени на его
разбор и понимание, поскольку сам код был перемешан с системой
шаблонов, а также был спроектирован с использованием различных
языковых нотаций.
Теперь это работает и работает быстро на множестве сайтов,
занимающихся электронной коммерцией. Также стоит отметить, что
у сообщества osCommerce есть форум и некое подобие структуры
для разработки новых модулей и расширений. Как следствие, для
системы можно найти очень много полезных "плагинов".
phpMyAdmin
phpMyAdmin напрямую использует только один класс: Mimer SQL
Validator, который в случае phpMyAdmin зависит от PEAR пакетов
Mail_Mime, Net_DIME и SOAP. Такое решение скорее всего было
основано на убеждении: "оно хорошо служит своей цели - давайте
его используем". Во всем остальном, похоже, что код приложения
написан в процедурном стиле и вновь мы имеем тесное связывание
HTML и PHP.
phpMyAdmin - это инструмент, который используется
ежедневно. Более того, для простых модификаций, автор статьи
поощряет своих клиентов в использовании phpMyAdmin как системы
администрирования контента (естественно при соответственном
ограничении их прав). phpMyAdmin работает очень хорошо и,
учитывая то, что ему приходится делать, он работает очень
быстро. Автор часто расширяет phpMyAdmin для некоторых
приложений, делая его системой администрирования, усиливая
такие его свойства как избранные запросы, для того чтобы
клиенты могли быстро и просто создавать выборки и
редактировать их. Сам же код должен быть достаточно хорошо
сопровождаемым, поскольку с каждым выходом новой версии
рассматриваемый продукт развивается все в более и более
практичный инструмент.
Процедурный подход -
заключение
Процедурные части обоих приложений хорошо документированы.
В случае osCommerce, предоставление определенного каркаса для
разработки расширений также способствует поддержке и
расширению этого продукта. Однако не существует никакого
подобия программного интерфейса (API), позволяющего расширить
продукт до чего-либо серьезно отличающегося от оригинала.
Потребуется очень много усилий, чтобы превратить osCommerce
в бухгалтерское приложение и еще больше, чтобы превратить
phpMyAdmin в настраиваемую систему администрирования контента.
Тем не менее, ту работу, для которой изначально были созданы
эти оба продукта - они выполняют очень хорошо.
Объектный подход
Одним из основных аргументов в пользу
объектно-ориентированного подхода является расширяемость и
модульность полученного кода. Сам по себе факт
программирования в объектно-ориентированом стиле не сможет
закомментировать код или создать документацию, но он может
подвигнуть вас к этому действию. И однажды стремление к
расширяемости создаваемого вами кода может привести вас к
написанию настоящего программного интерфейса (API) для вашего
приложения.
PHP5 обещает сделать процесс объектно-ориентированного
программирования гораздо более приятным. С известной долей
иронии можно сравнивать новую версию с выходом "Java 2",
поскольку она включает в себя ряд таких свойств, как
интерфейсы, ссылочная модель объектов и структуры try/catch,
уже зарекомендовавших себя в мире Java. Но даже для PHP4 и
более ранних версий можно найти примеры удачного применения
объектов.
Одним из самых удачных автор статьи считает Smarty -
систему шаблонов, позволяющих отделять код PHP от контента.
Другим полезным примером является свободно распространяемый
генератор PDF, полностью написанный на PHP и получивший
название FPDF. Оба программных продукта практикуют подход при
котором HTML код отделяется от PHP. Особенно важно отметить
тот факт, что обе программы могут быть расширены до других
полезных приложений.
Smarty
Smarty является инструментом для создания сложных форм и
сайтов, основанных на шаблонах. В большинстве случаев, когда
нужно построить прототип системы с полностью настраиваемым
внешним видом - Smarty самый подходящий инструмент. Внешний
вид сайта может быть полностью изменен, не затрагивая
функциональности, которая стоит за этим. Чтобы сделать процесс
смены оформления более простым для дизайнеров, автор создал
индивидуальную систему тегов, как расширение системы тегов
Smarty. Так, например, при вводе в начале страницы директивы
[navigation horizontal separated by " | "]
на сайте появится горизонтальный блок навигации,
разделенной между собой символами "|". Поскольку Smarty уже
обладает очень развитым механизмом отображения данных,
содержащихся в переменных, описанный процесс подразумевает
всего лишь отображение сложных тегов Smarty в простые
директивы для оформления.
Более подробно об этом можно прочесть на http://simpletags.sourceforge.net/ и http://simplequiz.sourceforge.net/.
Поскольку Smarty представляет собой класс и все его методы
хорошо документированы, то его функциональность можно легко
расширять. Кроме того
Smarty предоставляет уровень отделения области видимости
прочих переменных, используемых в PHP тем, что заставляет
передавать шаблоном только те переменные, которые вы
собираетесь использовать. Такого рода "экологический надзор за
переменными" позволяет создать надежную рабочую связь между
дизайнерами, создающими шаблоны и программистами, которые
предоставляют для них данные.
FPDF
FPDF - отличный инструмент. Если вы разочаровались в
постоянно изменяющемся API к pdflib и не хотите платить за
хорошо поддерживаемое приложение, если вы не можете управлять
расширениями на той версии PHP, с которой работаете (как это
бывает в случае хостинга с общими ресурсами) - задумайтесь над
использованием этого свободно распространяемого, полностью
написанного на PHP PDF-генератора.[1].
Этот класс хорошо документирован, включая массу приятных
примеров того как создавать и располгать в документе текст и
графику. Для уже упомятуго сайта онлайнового обучения автор
использовал FPDF для динамического создания сертификатов
окончания курсов, с использованием шрифтов семейтсва True Type
и графики с разрешением 300 dpi. Те дополнительные секунды,
требующиеся для вызова класса и исполнения операций с PDF
практически не заметны на фоне того, что сам PDF-файл иногда
требует нескольких минут для скачивания. Ввиду этого, на
низкоскоростных соединениях дополнительное время, затраченное
на создание и отправку PDF-файла порой оказывается меньше, чем
скачивание статического аналога.
Кроме того, поскольку FPDF основана на
объектно-ориентированном подходе - она может быть расширена.
Многие методы до конца не реализованы и просто существуют в
качестве структуры, которая может быть расширена вами,
например для создания собственных колонтитулов (шапок) в
отдельном классе.
Объектный подход -
заключение
Как Smarty так и FPDF предоставляют хорошо
документированный интерфейс для расширения их основного
класса. Заключение всех методов и данных в класс в данном
случае является хорошо продуманным решением. Подобной
функциональности можно было бы достичь, используя обычные
функции и глобальные переменные, но ее трудно было бы
расширить впоследствии. Кроме того невозможно было бы
создавать несколько сущностей с одинаковой функциональностью,
как это можно делать с объектами. Такие объекты могут задавать
стиль PDF или HTML документа, который вы создаете. Таким
образом вы можете публиковать одни и те же данные в различных
форматах.
Описанные выше приложения являются отличным примером
использования объектно-ориентированного подхода для создания
полезной библиотеки.
Когда и что
применять?
Мы рассмотрели примеры применения робких подходов для
разных целей и познакомились с эффективными примерами
применения этих подходов в реальных приложениях. Что же делать
если ваши цели требуют обоих подходов?
Общие проверки производительности объектного и процедурного
кода дадут вам очень отдаленное представление о том, что можно
ожидать от конкретного приложения, создаваемого вами. Вместо
подобных проверок выбирайте тот способ оценки, который больше
всего вам подходит для оценки действительной конкретного кода
на том или ином участке. Проверяйте свои функции на
возможность использования ими общих ресурсов. Продумайте
необходимость расширения таких функций. Подумайте насколько
написание класса поможет вам сделать ваш код расширяемым.
Кроме всего прочего, распределите ваши цели по степени
приоритета. Взвесьте их. Если вы будете использовать
практический подход от проекта к проекту для определения
эффективности объектно-ориентированного или процедурного кода,
вам гарантировано непредвзятое и подкрепленное вашим опытом
решение. И помните: используя PHP, у вас всегда есть шанс. Вы
всегда сможете комбинировать оба подхода.
Почему оба подхода
необходимы?
Возвращаясь к фанатам объектного и процедурного подходов,
мы не забудем отметить и их заслуги.
- Поблагодарим "объектных" программистов за практичность и
расширяемость Smarty и FPDF
- Поблагодарим "процедурных" программистов за быстроту и
надежность функционирующие osCommerce и phpMyAdmin.
На самом деле эти признания идут гораздо дальше - к основам
PHP.
Как PECL так и PEAR уже снискали себе достаточно славы и
критики. Оба проекта могут служить хорошей иллюстрацией
различий между процедурным и объектно-ориентированным
программированием.
PECL предоставляет расширения для PHP, написанные на C, с
использованием процедурного подхода и ориентированные на
скорость и лаконичность кода. Зачастую это перенос
существующих приложений с лицензией LGPL (Lesser General
Public License). Таким способом за несколько лет в PHP была
добавлена масса интересных функций. Кроме того, не стоит
забывать, что и сам PHP написан на C.
PEAR способствовал появлению массы полезных и интересных
классов, начиная от создания Excel-таблиц и заканчивая
изменением DNS-записей. Используя библиотеки PEAR, вы
экономите себе массу времени и можете создавать
функциональность, которая способна заставит вас воскликнуть:
"Я даже не знал, что PHP может такое!"
Заключение
Автор надеется, что эта статья дала вам возможность по
достоинству оценить полезность каждого из подходов
программирования и, что более важно, снабдила вас информацией
и желанием для более глубокого изучения этой проблемы. Думайте
о своих целях, тестируйте реальные примеры и относитесь к
радикальным суждениям с известной долей скептицизма. Оба
подхода имеют свои преимущества. Поэтому идите и напишите пару
строчек хорошего кода!